sonamu 0.8.21 → 0.8.23
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/dist/api/config.d.ts +1 -1
- package/dist/api/config.d.ts.map +1 -1
- package/dist/api/config.js +1 -1
- package/dist/api/sonamu.d.ts +1 -1
- package/dist/api/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +14 -19
- package/dist/cone/cone-generator.js +29 -16
- package/dist/database/base-model.js +2 -2
- package/dist/database/db.d.ts +1 -1
- package/dist/database/db.d.ts.map +1 -1
- package/dist/database/db.js +1 -1
- package/dist/syncer/syncer.js +2 -2
- package/dist/testing/dev-test-routes.d.ts.map +1 -1
- package/dist/testing/dev-test-routes.js +4 -1
- package/dist/testing/global-setup.d.ts.map +1 -1
- package/dist/testing/global-setup.js +1 -4
- package/dist/ui/api.d.ts.map +1 -1
- package/dist/ui/api.js +11 -11
- package/dist/ui-web/assets/index-BrQKU3j9.css +1 -0
- package/dist/ui-web/assets/{index-DP968oXY.js → index-DIasN3Gt.js} +47 -47
- package/dist/ui-web/index.html +2 -2
- package/package.json +3 -3
- package/src/api/config.ts +1 -1
- package/src/api/sonamu.ts +15 -20
- package/src/cone/cone-generator.ts +39 -19
- package/src/database/base-model.ts +1 -1
- package/src/database/db.ts +1 -1
- package/src/skills/AGENTS.md +11 -1
- package/src/skills/sonamu/SKILL.md +42 -149
- package/src/skills/sonamu/cdd.md +172 -517
- package/src/skills/sonamu/cone.md +3 -4
- package/src/skills/sonamu/fixture-cli.md +1 -1
- package/src/skills/sonamu/workflow.md +72 -80
- package/src/syncer/syncer.ts +1 -1
- package/src/testing/dev-test-routes.ts +3 -0
- package/src/testing/global-setup.ts +0 -4
- package/src/ui/api.ts +6 -15
- package/dist/ui-web/assets/index-B7gc0Ygb.css +0 -1
- package/src/skills/project/business-logic.md +0 -270
- package/src/skills/project/requirements.md +0 -160
package/dist/ui-web/index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/sonamu-ui/setting.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>{{projectName}}: Sonamu UI</title>
|
|
8
|
-
<script type="module" crossorigin src="/sonamu-ui/assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/sonamu-ui/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/sonamu-ui/assets/index-DIasN3Gt.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/sonamu-ui/assets/index-BrQKU3j9.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sonamu",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.23",
|
|
4
4
|
"description": "Sonamu — TypeScript Fullstack API Framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -124,9 +124,9 @@
|
|
|
124
124
|
"vite": "7.3.0",
|
|
125
125
|
"vitest": "^4.0.10",
|
|
126
126
|
"@sonamu-kit/hmr-hook": "^0.4.1",
|
|
127
|
-
"@sonamu-kit/
|
|
127
|
+
"@sonamu-kit/hmr-runner": "^0.1.1",
|
|
128
128
|
"@sonamu-kit/ts-loader": "^2.1.3",
|
|
129
|
-
"@sonamu-kit/
|
|
129
|
+
"@sonamu-kit/tasks": "^0.2.0"
|
|
130
130
|
},
|
|
131
131
|
"devDependencies": {
|
|
132
132
|
"@biomejs/biome": "^2.3.13",
|
package/src/api/config.ts
CHANGED
|
@@ -83,7 +83,7 @@ export type SonamuConfig<TSinkId extends string = string, TFilterId extends stri
|
|
|
83
83
|
targets: string[]; // "web", "app" 등
|
|
84
84
|
};
|
|
85
85
|
|
|
86
|
-
database
|
|
86
|
+
database: {
|
|
87
87
|
// 데이터베이스(pg는 pg 모듈, pgnative는 pg-native 모듈의 설치가 필요합니다.)
|
|
88
88
|
database?: "pg" | "pgnative";
|
|
89
89
|
// 기본 데이터베이스 이름
|
package/src/api/sonamu.ts
CHANGED
|
@@ -144,7 +144,11 @@ class SonamuClass {
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
private _workflows: WorkflowManager | null = null;
|
|
147
|
-
get workflows(): WorkflowManager
|
|
147
|
+
get workflows(): WorkflowManager {
|
|
148
|
+
if (this._workflows === null) {
|
|
149
|
+
throw new Error("Sonamu has not been initialized");
|
|
150
|
+
}
|
|
151
|
+
|
|
148
152
|
return this._workflows;
|
|
149
153
|
}
|
|
150
154
|
|
|
@@ -200,10 +204,8 @@ class SonamuClass {
|
|
|
200
204
|
const { loadConfig } = await import("./config");
|
|
201
205
|
this.config = await loadConfig(this.apiRootPath);
|
|
202
206
|
// sonamu.config.ts 기본값 설정
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
this.config.database.defaultOptions.client = this.config.database.database ?? "pg";
|
|
206
|
-
}
|
|
207
|
+
this.config.database.database = this.config.database.database ?? "pg";
|
|
208
|
+
this.config.database.defaultOptions.client = this.config.database.database ?? "pg";
|
|
207
209
|
|
|
208
210
|
// 로깅 설정
|
|
209
211
|
const { configureLogTape } = await import("../logger/configure");
|
|
@@ -214,13 +216,11 @@ class SonamuClass {
|
|
|
214
216
|
}
|
|
215
217
|
|
|
216
218
|
// DB 로드
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
console.log(chalk.green("DB Config Loaded!"));
|
|
223
|
-
}
|
|
219
|
+
const { DB } = await import("../database/db");
|
|
220
|
+
this.dbConfig = DB.generateDBConfig(this.config.database);
|
|
221
|
+
if (!doSilent) {
|
|
222
|
+
const chalk = (await import("chalk")).default;
|
|
223
|
+
console.log(chalk.green("DB Config Loaded!"));
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
// Entity 로드
|
|
@@ -1306,11 +1306,6 @@ class SonamuClass {
|
|
|
1306
1306
|
}
|
|
1307
1307
|
|
|
1308
1308
|
private async initializeWorkflows(options: SonamuTaskOptions | undefined) {
|
|
1309
|
-
// database 설정이 없으면 WorkflowManager를 초기화하지 않음
|
|
1310
|
-
if (!this.config.database) {
|
|
1311
|
-
return;
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
1309
|
const { WorkflowManager } = await import("../tasks/workflow-manager");
|
|
1315
1310
|
// NOTE: @sonamu-kit/tasks 안에선 knex config를 수정하기 때문에 connection이 아닌 config 째로 보냅니다.
|
|
1316
1311
|
this._workflows = new WorkflowManager(DB.getDBConfig("w"));
|
|
@@ -1326,7 +1321,7 @@ class SonamuClass {
|
|
|
1326
1321
|
};
|
|
1327
1322
|
|
|
1328
1323
|
if (enableWorker) {
|
|
1329
|
-
this.
|
|
1324
|
+
this.workflows.setupWorker({
|
|
1330
1325
|
...defaultWorkerOptions,
|
|
1331
1326
|
...options.workerOptions,
|
|
1332
1327
|
});
|
|
@@ -1339,7 +1334,7 @@ class SonamuClass {
|
|
|
1339
1334
|
|
|
1340
1335
|
server.addHook("onClose", async () => {
|
|
1341
1336
|
await options.lifecycle?.onShutdown?.(server);
|
|
1342
|
-
await this.workflows
|
|
1337
|
+
await this.workflows.destroy();
|
|
1343
1338
|
await this.destroy();
|
|
1344
1339
|
});
|
|
1345
1340
|
|
|
@@ -1363,7 +1358,7 @@ class SonamuClass {
|
|
|
1363
1358
|
server
|
|
1364
1359
|
.listen({ port, host })
|
|
1365
1360
|
.then(async () => {
|
|
1366
|
-
await this.workflows
|
|
1361
|
+
await this.workflows.startWorker();
|
|
1367
1362
|
await options.lifecycle?.onStart?.(server);
|
|
1368
1363
|
})
|
|
1369
1364
|
.catch(async (err) => {
|
|
@@ -84,35 +84,55 @@ function getApiKey(): string {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
|
-
*
|
|
87
|
+
* 도메인별 {domain}.contract.md와 architecture.md를 읽어 컨텍스트로 반환합니다.
|
|
88
88
|
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
89
|
+
* - contract/{domain}/{domain}.contract.md: 도메인 규칙과 결정 근거 (주 참조 대상)
|
|
90
|
+
* - .claude/skills/project/architecture.md: 엔티티 설계 구조 (보조 참조)
|
|
91
|
+
*
|
|
92
|
+
* cone 생성 시 LLM에게 전달하여 도메인 맥락에 맞는 메타데이터를 생성하도록 합니다.
|
|
91
93
|
*/
|
|
92
94
|
function readProjectSkills(): string {
|
|
93
95
|
try {
|
|
94
96
|
const { Sonamu } = require("../api");
|
|
95
97
|
const projectRoot = Sonamu.appRootPath;
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
if (!fs.existsSync(skillsDir)) {
|
|
99
|
-
return "";
|
|
100
|
-
}
|
|
98
|
+
const contents: string[] = [];
|
|
101
99
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
100
|
+
// contract/**/*.contract.md 수집
|
|
101
|
+
const contractDir = path.join(projectRoot, "contract");
|
|
102
|
+
if (fs.existsSync(contractDir)) {
|
|
103
|
+
const domains = fs
|
|
104
|
+
.readdirSync(contractDir, { withFileTypes: true })
|
|
105
|
+
.filter((d) => d.isDirectory())
|
|
106
|
+
.map((d) => d.name);
|
|
107
|
+
|
|
108
|
+
for (const domain of domains) {
|
|
109
|
+
const domainDir = path.join(contractDir, domain);
|
|
110
|
+
const contractFiles = fs
|
|
111
|
+
.readdirSync(domainDir)
|
|
112
|
+
.filter((f: string) => f.endsWith(".contract.md"));
|
|
113
|
+
|
|
114
|
+
for (const file of contractFiles) {
|
|
115
|
+
const filePath = path.join(domainDir, file);
|
|
116
|
+
const content = fs.readFileSync(filePath, "utf-8").trim();
|
|
117
|
+
if (content) {
|
|
118
|
+
contents.push(`--- contract/${domain}/${file} ---\n${content}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
108
122
|
}
|
|
109
123
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
124
|
+
// .claude/skills/project/architecture.md 보조 참조
|
|
125
|
+
const architecturePath = path.join(
|
|
126
|
+
projectRoot,
|
|
127
|
+
".claude",
|
|
128
|
+
"skills",
|
|
129
|
+
"project",
|
|
130
|
+
"architecture.md",
|
|
131
|
+
);
|
|
132
|
+
if (fs.existsSync(architecturePath)) {
|
|
133
|
+
const content = fs.readFileSync(architecturePath, "utf-8").trim();
|
|
114
134
|
if (content) {
|
|
115
|
-
contents.push(`---
|
|
135
|
+
contents.push(`--- architecture.md ---\n${content}`);
|
|
116
136
|
}
|
|
117
137
|
}
|
|
118
138
|
|
|
@@ -415,7 +415,7 @@ export class BaseModelClass<
|
|
|
415
415
|
const { default: SqlParser } = await import("node-sql-parser");
|
|
416
416
|
const parser = new SqlParser.Parser();
|
|
417
417
|
const parsedQuery = parser.astify(countPuri.toQuery(), {
|
|
418
|
-
database: Sonamu.config.database
|
|
418
|
+
database: Sonamu.config.database.database,
|
|
419
419
|
});
|
|
420
420
|
|
|
421
421
|
const leftJoinTables = getJoinTables(parsedQuery, ["LEFT JOIN"]);
|
package/src/database/db.ts
CHANGED
|
@@ -157,7 +157,7 @@ export class DBClass {
|
|
|
157
157
|
this.workerDBs.clear();
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
-
public generateDBConfig(config:
|
|
160
|
+
public generateDBConfig(config: SonamuConfig["database"]): SonamuDBConfig {
|
|
161
161
|
const defaultKnexConfig: Partial<DatabaseConfig> = assign(
|
|
162
162
|
{
|
|
163
163
|
client: "postgresql",
|
package/src/skills/AGENTS.md
CHANGED
|
@@ -68,4 +68,14 @@ Read the listed skill file before attempting any workaround or fix in these situ
|
|
|
68
68
|
| Writing or modifying a `@upload` method | `api.md` |
|
|
69
69
|
| Database query returning unexpected results | `puri.md` |
|
|
70
70
|
| Migration error or schema change | `migration.md` |
|
|
71
|
-
|
|
|
71
|
+
| Starting AC+Claim-based development or writing a Claim | `cdd.md` |
|
|
72
|
+
| Applying Auth Guards or handling session/permission logic | `auth.md` |
|
|
73
|
+
| Implementing polymorphic relations with `entity_type` + `entity_id` pattern | `entity-relations.md` |
|
|
74
|
+
| Writing Puri SELECT / WHERE / JOIN / FTS / pgvector queries | `puri.md` |
|
|
75
|
+
| Implementing `@upload` file upload or deciding parameter pattern | `api.md`, `framework-change.md` |
|
|
76
|
+
| Reading or writing files under `contract/rules/` | `cdd.md` |
|
|
77
|
+
| Implementing `BaseAgentClass` or `@tools` decorator | `ai-agents.md` |
|
|
78
|
+
| Implementing background jobs or cron scheduling | `tasks.md` |
|
|
79
|
+
| Batch-saving relation data (upsert) | `upsert.md` |
|
|
80
|
+
| Adding a new entity or enum and registering i18n keys | `i18n.md` |
|
|
81
|
+
| Implementing pgvector embeddings or vector search | `vector.md` |
|
|
@@ -26,13 +26,13 @@ Sonamu 프레임워크로 프로젝트를 개발하기 위한 Claude Code skill
|
|
|
26
26
|
## 개발 흐름
|
|
27
27
|
|
|
28
28
|
```
|
|
29
|
-
PHASE 0: 프로젝트 생성 및 초기 설정 (프로젝트 생성 →
|
|
30
|
-
PHASE
|
|
31
|
-
PHASE
|
|
32
|
-
PHASE
|
|
33
|
-
PHASE
|
|
34
|
-
PHASE
|
|
35
|
-
PHASE
|
|
29
|
+
PHASE 0: 프로젝트 생성 및 초기 설정 (프로젝트 생성 → 도메인 식별 → auth generate → Users 시퀀스 설정)
|
|
30
|
+
PHASE 1: 도메인 Logic 문서화 (도메인별 contract/{domain}/*.contract.md 작성 → 사용자 확인 완료)
|
|
31
|
+
PHASE 2: 엔티티 설계 (사용자와 함께 설계 확인)
|
|
32
|
+
PHASE 3: 엔티티 생성 및 마이그레이션 (entity.json + migration + cone + scaffolding)
|
|
33
|
+
PHASE 4: 테스트 및 API 구현 (contract→Claim→AC→implement 반복)
|
|
34
|
+
PHASE 5: Fixture 생성 (사용자 승인 후 LLM으로 생성)
|
|
35
|
+
PHASE 6: Frontend 개발 (batch별 진행, 사용자 확인)
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
**상세 내용:** `workflow.md` 참조
|
|
@@ -44,14 +44,14 @@ PHASE 5: Frontend 개발 (batch별 진행, 사용자 확인)
|
|
|
44
44
|
| 사용자 지시 | 시작 PHASE | 전제 조건 확인 |
|
|
45
45
|
|------------|------------|----------------|
|
|
46
46
|
| "새 프로젝트 만들어줘" | PHASE 0 | — |
|
|
47
|
-
| "contract 작성해줘" / "
|
|
48
|
-
| "엔티티 추가해줘" / "엔티티 생성해줘" | PHASE
|
|
49
|
-
| "테스트 작성해줘" / "API 구현해줘" | PHASE
|
|
50
|
-
| "fixture 생성해줘" | PHASE
|
|
51
|
-
| "프론트엔드 개발해줘" | PHASE
|
|
47
|
+
| "contract 작성해줘" / "도메인 분석해줘" | PHASE 1 | 도메인 목록 식별 완료 |
|
|
48
|
+
| "엔티티 추가해줘" / "엔티티 생성해줘" | PHASE 2 | 프로젝트 존재, dev 서버 실행 중, contract/**/*.contract.md 읽기, PHASE 1 완료 |
|
|
49
|
+
| "테스트 작성해줘" / "API 구현해줘" | PHASE 4 | entity.json 존재, migration 완료, scaffolding 완료 |
|
|
50
|
+
| "fixture 생성해줘" | PHASE 5 | 테스트 통과, cone.note 존재 확인 |
|
|
51
|
+
| "프론트엔드 개발해줘" | PHASE 6 | API 구현 완료, contract/**/*.contract.md 확인 |
|
|
52
52
|
|
|
53
53
|
**중간 진입 시 규칙:**
|
|
54
|
-
1. `skills/project
|
|
54
|
+
1. `contract/**/*.contract.md` 및 `skills/project/architecture.md`가 있으면 반드시 먼저 읽는다
|
|
55
55
|
2. 해당 PHASE의 전제 조건이 충족되었는지 확인한다
|
|
56
56
|
3. 충족되지 않으면 사용자에게 알리고 필요한 단계부터 진행한다
|
|
57
57
|
4. 해당 PHASE 내에서는 `workflow.md`의 넘버링된 Step을 순서대로 진행한다. 어떤 Step도 건너뛰지 않고, 자체 판단으로 병합하지 않는다
|
|
@@ -62,159 +62,52 @@ PHASE 5: Frontend 개발 (batch별 진행, 사용자 확인)
|
|
|
62
62
|
|
|
63
63
|
### CRITICAL: 작업 시작 전 필수 확인
|
|
64
64
|
|
|
65
|
-
**프로젝트 작업을 시작하기 전에 반드시
|
|
65
|
+
**프로젝트 작업을 시작하기 전에 반드시 다음 문서를 읽으세요.**
|
|
66
66
|
|
|
67
67
|
```
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
└── business-logic.md # 사용자 권한별 비즈니스 로직
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
### 문서 작성 시점
|
|
75
|
-
|
|
76
|
-
#### 1. requirements.md
|
|
77
|
-
**언제:** 사용자로부터 요구사항을 받았을 때
|
|
78
|
-
**내용:**
|
|
79
|
-
- 프로젝트 목표 및 배경
|
|
80
|
-
- 기능 요구사항 (FR)
|
|
81
|
-
- 비기능 요구사항 (NFR)
|
|
82
|
-
- 제약사항 및 전제조건
|
|
83
|
-
|
|
84
|
-
**예시:**
|
|
85
|
-
```markdown
|
|
86
|
-
# 요구사항 정의
|
|
87
|
-
|
|
88
|
-
## 프로젝트 목표
|
|
89
|
-
온라인 병원 예약 시스템
|
|
68
|
+
contract/
|
|
69
|
+
└── {domain}/
|
|
70
|
+
└── {domain}.contract.md # PHASE 1: 도메인 규칙 + 결정 근거 (영구 문서, 코드 변경 시 함께 갱신)
|
|
90
71
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
2. 의사가 진료 일정을 관리할 수 있어야 함
|
|
94
|
-
...
|
|
72
|
+
.claude/skills/project/
|
|
73
|
+
└── architecture.md # 엔티티 설계 + 시스템 아키텍처
|
|
95
74
|
|
|
96
|
-
|
|
97
|
-
- 관리자: 시스템 전체 관리
|
|
98
|
-
- 의사: 진료 일정, 환자 기록 관리
|
|
99
|
-
- 환자: 예약, 진료 내역 조회
|
|
100
|
-
- 병원: 의사 관리, 통계 확인
|
|
75
|
+
tmp/claims/ # 진행 중 Claim YAML (완료 후 폐기)
|
|
101
76
|
```
|
|
102
77
|
|
|
103
|
-
|
|
104
|
-
**언제:** 엔티티를 설계하거나 시스템 아키텍처를 논의할 때
|
|
105
|
-
**내용:**
|
|
106
|
-
- Entity 구조 및 관계 설계
|
|
107
|
-
- 데이터베이스 스키마
|
|
108
|
-
- API 엔드포인트 설계
|
|
109
|
-
- 시스템 컴포넌트 구조
|
|
78
|
+
**Ground truth는 코드다.** `*.contract.md`는 코드 결정의 근거 기록이지 선행 정의서가 아니다. 코드와 `*.contract.md`가 충돌하면 코드를 우선한다.
|
|
110
79
|
|
|
111
|
-
|
|
112
|
-
```markdown
|
|
113
|
-
# 시스템 아키텍처
|
|
114
|
-
|
|
115
|
-
## 엔티티 설계
|
|
116
|
-
|
|
117
|
-
### User (사용자)
|
|
118
|
-
- id: number (PK)
|
|
119
|
-
- username: string
|
|
120
|
-
- email: string
|
|
121
|
-
- role: enum (admin, doctor, patient, hospital)
|
|
122
|
-
|
|
123
|
-
### Doctor (의사)
|
|
124
|
-
- id: number (PK)
|
|
125
|
-
- user_id: number (FK → User)
|
|
126
|
-
- specialty: string
|
|
127
|
-
- hospital_id: number (FK → Hospital)
|
|
128
|
-
|
|
129
|
-
### Appointment (예약)
|
|
130
|
-
- id: number (PK)
|
|
131
|
-
- patient_id: number (FK → User)
|
|
132
|
-
- doctor_id: number (FK → Doctor)
|
|
133
|
-
- appointment_date: datetime
|
|
134
|
-
- status: enum (pending, confirmed, cancelled)
|
|
135
|
-
|
|
136
|
-
## 관계
|
|
137
|
-
- User ← Doctor (BelongsToOne)
|
|
138
|
-
- User ← Appointment.patient (BelongsToOne)
|
|
139
|
-
- Doctor ← Appointment.doctor (BelongsToOne)
|
|
140
|
-
```
|
|
80
|
+
### 도메인 `*.contract.md`
|
|
141
81
|
|
|
142
|
-
|
|
143
|
-
**언제:** 사용자 권한별 비즈니스 로직을 정의할 때
|
|
144
|
-
**내용:**
|
|
145
|
-
- 각 사용자 유형(role)별 권한
|
|
146
|
-
- 비즈니스 규칙 및 제약사항
|
|
147
|
-
- 상태 전이 규칙
|
|
148
|
-
- 검증 로직
|
|
82
|
+
도메인 규칙을 응집된 형태로 기술하고, 코드만으로는 파악하기 어려운 결정 근거를 함께 기록한다.
|
|
149
83
|
|
|
150
|
-
**예시:**
|
|
151
84
|
```markdown
|
|
152
|
-
# 비즈니스 로직
|
|
153
|
-
|
|
154
|
-
## 사용자 권한
|
|
155
|
-
|
|
156
|
-
### 관리자 (admin)
|
|
157
|
-
- 모든 데이터 CRUD 가능
|
|
158
|
-
- 사용자 role 변경 가능
|
|
159
|
-
- 시스템 설정 관리
|
|
160
|
-
|
|
161
|
-
### 의사 (doctor)
|
|
162
|
-
- 자신의 일정 관리 (CRUD)
|
|
163
|
-
- 예약 확인/취소
|
|
164
|
-
- 환자 진료 기록 조회/작성
|
|
165
|
-
- 다른 의사 데이터 조회 불가
|
|
166
|
-
|
|
167
|
-
### 환자 (patient)
|
|
168
|
-
- 의사 검색 및 조회
|
|
169
|
-
- 예약 생성/조회/취소 (자신의 예약만)
|
|
170
|
-
- 자신의 진료 기록 조회
|
|
171
|
-
- 다른 환자 데이터 접근 불가
|
|
172
|
-
|
|
173
|
-
### 병원 (hospital)
|
|
174
|
-
- 소속 의사 관리
|
|
175
|
-
- 병원 통계 조회
|
|
176
|
-
- 소속 의사의 예약 현황 조회
|
|
177
|
-
|
|
178
|
-
## 예약 상태 전이
|
|
179
|
-
pending → confirmed (의사 확인)
|
|
180
|
-
pending → cancelled (환자/의사 취소)
|
|
181
|
-
confirmed → cancelled (환자/의사 취소, 24시간 전까지만)
|
|
182
|
-
|
|
183
|
-
## 비즈니스 규칙
|
|
184
|
-
1. 예약은 진료 24시간 전까지만 취소 가능
|
|
185
|
-
2. 의사는 동시에 2개 이상 예약 불가
|
|
186
|
-
3. 환자는 같은 의사에게 1주일에 1번만 예약 가능
|
|
187
|
-
```
|
|
85
|
+
# {도메인} 비즈니스 로직
|
|
188
86
|
|
|
189
|
-
|
|
87
|
+
## 규칙
|
|
88
|
+
- 환불은 결제 후 7일 이내만 가능 [근거: PG사 정책]
|
|
89
|
+
- 주문 상태 전환: 대기 → 확인 → 배송 → 완료
|
|
190
90
|
|
|
191
|
-
|
|
91
|
+
## 워크플로우
|
|
92
|
+
1. ...
|
|
93
|
+
```
|
|
192
94
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
95
|
+
**두 가지 개발 경로:**
|
|
96
|
+
- **신규**: `*.contract.md` 작성 → Claim → AC(테스트명) → implement (TDD 방식)
|
|
97
|
+
- **변경**: 코드 수정 → Claim 등록 → `*.contract.md` 확인/갱신 (변경 근거 기록)
|
|
196
98
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
99
|
+
**갱신 규칙:**
|
|
100
|
+
- 코드 변경 시 영향받는 도메인 규칙을 확인하고 `*.contract.md`도 함께 업데이트
|
|
101
|
+
- 변경 이유와 결정 근거를 함께 기록 — 이것이 `*.contract.md`를 살아있게 유지
|
|
200
102
|
|
|
201
|
-
|
|
202
|
-
**변경 내용:** status 필드 추가 (pending, confirmed, cancelled)
|
|
203
|
-
**이유:** 예약 취소 기능 추가 요청
|
|
204
|
-
**영향:** Appointment 엔티티, API 응답 구조 변경
|
|
205
|
-
```
|
|
103
|
+
### architecture.md
|
|
206
104
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
- 권한 규칙 변경 → business-logic.md 업데이트
|
|
210
|
-
- 요구사항 추가/변경 → requirements.md 업데이트
|
|
105
|
+
**언제:** 엔티티를 설계하거나 시스템 아키텍처를 논의할 때
|
|
106
|
+
**내용:** Entity 구조 및 관계 설계, 데이터베이스 스키마, 시스템 컴포넌트 구조
|
|
211
107
|
|
|
212
108
|
### Compacting 후에도 안전
|
|
213
109
|
|
|
214
|
-
|
|
215
|
-
- 대화가 압축(compacting)되어도 프로젝트 맥락 유지
|
|
216
|
-
- 언제든 문서를 다시 읽어 일관성 있게 작업 가능
|
|
217
|
-
- 설계 결정의 히스토리 추적 가능
|
|
110
|
+
문서가 파일로 영속화되어 있어서 대화가 압축(compacting)되어도 프로젝트 맥락이 유지된다.
|
|
218
111
|
|
|
219
112
|
---
|
|
220
113
|
|
|
@@ -223,7 +116,7 @@ confirmed → cancelled (환자/의사 취소, 24시간 전까지만)
|
|
|
223
116
|
| Skill | 파일 | 용도 |
|
|
224
117
|
|-------|------|------|
|
|
225
118
|
| **전체 워크플로우** | `workflow.md` | **엔티티 설계 → Frontend 개발 7단계 가이드** |
|
|
226
|
-
| **CDD (
|
|
119
|
+
| **CDD (AC+Claim 기반 개발)** | `cdd.md` | **`*.contract.md`(도메인 규칙), AC(테스트 이름), Claim(작업 지시서) 3종 체계** |
|
|
227
120
|
| 프로젝트 생성 | `create-sonamu.md` | create-sonamu CLI 옵션 |
|
|
228
121
|
| 프로젝트 초기화 | `project-init.md` | 프로젝트 생성 여부 확인, 대화 흐름 |
|
|
229
122
|
| 프로젝트 설정 | `config.md` | .env, sonamu.config.ts 설정 |
|
|
@@ -262,7 +155,7 @@ confirmed → cancelled (환자/의사 취소, 24시간 전까지만)
|
|
|
262
155
|
| 작업 | 참고 Skill |
|
|
263
156
|
|------|-----------|
|
|
264
157
|
| **처음부터 전체 시스템 개발** | **workflow.md (7단계 마스터 가이드)** |
|
|
265
|
-
|
|
|
158
|
+
| **도메인 Logic 문서화 / AC+Claim 기반 개발** | **cdd.md** |
|
|
266
159
|
| 프로젝트 생성 | create-sonamu, project-init |
|
|
267
160
|
| 프로젝트 설정 | config |
|
|
268
161
|
| Sonamu 로컬 개발 설정 | config |
|