@simplysm/sd-claude 14.0.95 → 14.0.96

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.
@@ -7,7 +7,7 @@
7
7
  common 패키지에 클라이언트별 `AppStructureItem[]` 상수를 두고, 앱 부트스트랩에서 `SdAppStructureProvider.initialize(items)` 로 연결함.
8
8
 
9
9
  ```ts
10
- // common/src/app-structure-items.ts
10
+ // common/src/app-structure.ts
11
11
  import type { AppStructureItem } from "@simplysm/service-common";
12
12
  import { tablerBox } from "@ng-icons/tabler-icons"; // 아이콘은 @ng-icons 의 SVG 문자열 상수
13
13
 
@@ -504,7 +504,11 @@ constructor() {
504
504
 
505
505
  ## detail 데이터 흐름
506
506
 
507
- detail 컴포넌트는 식별자를 받아 자체 로드·저장을 수행하고, 변경·삭제 `submitted` 부모에게 알림.
507
+ detail 컴포넌트는 식별자를 받아 자체 로드·저장을 수행하고, 저장·삭제 결과를 부모에게 알림. 알림 output 은 사용 맥락에 따라 두 종류이며 **서로 독립**(배타 아님):
508
+
509
+ - **`submitted`** (`output<boolean>`) — 트리거: detail 을 컨트롤로 임베드한 부모가 저장을 감지해 refresh 해야 할 때. 부모가 `(submitted)="doRefresh()"` 로 받음.
510
+ - **`close`** (`SdModalContentDef` 의 output) — 트리거: detail 을 모달로 띄울 때. 저장·삭제 후 `close.emit(payload)` 로 결과를 반환하고, 호출 측은 `showAsync` 반환값으로 받아 refresh ([모달 호출](#모달-호출) 참조).
511
+ - 한 detail 이 모달·임베드 양쪽으로 쓰이면 둘 다 둠. 모달 전용이면 `close` 만, 임베드 전용이면 `submitted` 만, page 전용·공유데이터 뷰면 둘 다 없이 self-refresh·`emitAsync` 로 처리. 아래 시그널·예시는 `submitted`(임베드) 기준.
508
512
 
509
513
  ### 시그널 구성
510
514
 
@@ -601,11 +605,12 @@ async onSubmit(): Promise<void> {
601
605
 
602
606
  **핵심 약속**:
603
607
 
604
- - 식별자는 `input.required<>`받음.
608
+ - 식별자 input 은 조회·수정 전용이면 `input.required<number>()`, 신규 등록까지 한 detail 처리하면 `input<number>()`(undefined = 신규).
605
609
  - 로드 후 `_orgData = obj.clone(loaded)` 로 원본 보관.
606
610
  - 페이지 이탈 가드는 `setupCanDeactivate` + `obj.equal` 비교로 처리.
607
611
  - 저장 완료 후 `_refresh()` 로 다시 로드 → `submitted.emit(true)`.
608
612
  - 삭제·취소 등 다른 액션도 끝에 `submitted.emit(true)` 를 emit 해 부모(list) 가 새로고침할 수 있게 함.
613
+ - 위 `submitted` 는 임베드(컨트롤) 사용 시의 통지. 모달로 띄우는 detail 은 대신 `close.emit(payload)` 로 결과를 반환(호출 측이 `showAsync` 반환으로 refresh). 둘은 독립이라 양쪽으로 쓰이면 함께 둠.
609
614
 
610
615
  ## 시트 컬럼·셀 표준
611
616
 
@@ -734,7 +739,7 @@ private readonly _appService = inject(AppServiceProvider);
734
739
 
735
740
  await this._appService.user.someMethod(...);
736
741
 
737
- const listenerKey = await this._appService.authInfoEvent.addListener(info, async (data) => { ... });
742
+ const listenerKey = await this._appService.authInfoChangedEvent.addListener(info, async () => { ... });
738
743
  ```
739
744
 
740
745
  Provider 정의·서비스·이벤트 호출 추가 컨벤션은 [client-service.md](./client-service.md) 참조.
@@ -19,6 +19,7 @@
19
19
  [selectMode]="selectMode() ?? 'multi'"
20
20
  [key]="'<도메인-키>'"
21
21
  [items]="items()"
22
+ [currDeletedItems]="deletedItems()"
22
23
  [trackByFn]="trackByFn"
23
24
  [(selectedKeys)]="selectedKeys"
24
25
  [(currentPage)]="page"
@@ -39,6 +40,8 @@
39
40
  </sd-crud-list>
40
41
  ```
41
42
 
43
+ - **`[currDeletedItems]`** — 삭제(soft delete)된 행을 시트에서 취소선으로 구분하고 "선택 복구" 버튼을 띄우는 입력. `deletedItems = computed(() => this.items().filter((i) => i.isDeleted))` 를 넘김. 삭제항목 포함 검색을 지원하는 목록에는 필수 — 빠뜨리면 삭제 행이 일반 행과 구분되지 않고 복구 버튼이 나오지 않음.
44
+
42
45
  ### 슬롯 규약
43
46
 
44
47
  | 슬롯 | 용도 |
@@ -114,7 +117,7 @@ async onEdit(item: IItem, event: Event): Promise<void> {
114
117
  ```html
115
118
  <ng-template #toolTpl>
116
119
  <sd-button [size]="'sm'" [theme]="'link-success'" (click)="onDownloadExcelButtonClick()">
117
- <ng-icon [svg]="tablerUpload" />
120
+ <ng-icon [svg]="tablerDownload" />
118
121
  엑셀 다운로드
119
122
  </sd-button>
120
123
  </ng-template>
@@ -209,7 +212,7 @@ async onUploadExcelButtonClick(): Promise<void> {
209
212
  const dbRows =
210
213
  ids.length === 0
211
214
  ? []
212
- : await this._appOrm.connectWithoutTransAsync((db) =>
215
+ : await this._appOrm.connectAsync((db) =>
213
216
  db
214
217
  .employee()
215
218
  .where((c) => [expr.in(c.id, ids)])
@@ -359,7 +362,7 @@ async onDelete(targets: IItem[]): Promise<void> {
359
362
  }
360
363
  ```
361
364
 
362
- **단건 삭제 (detail)**: `sd-crud-detail` 표준 호출에는 `(delete)` output 이 없으므로, 삭제 버튼을 `#commandTpl` 슬롯에 두고 `(click)="onDelete()"` 로 배선. 목록의 `_refresh()` 대신 `submitted.emit(true)` 부모(list) 에 통지.
365
+ **단건 삭제 (detail)**: `sd-crud-detail` 표준 호출에는 `(delete)` output 이 없으므로, 삭제 버튼을 슬롯에 직접 둠 — 모달로 띄우는 detail 은 `#bottomCommandTpl`(모달 "확인" 버튼과 같은 하단 줄)에 두고 `(click)="onDelete()"` 로 배선. 목록의 `_refresh()` 대신 detail 통지 output(임베드면 `submitted.emit(true)`, 모달이면 `close.emit(payload)`)으로 부모(list) 에 통지.
363
366
 
364
367
  ```ts
365
368
  async onDelete(): Promise<void> {
@@ -435,4 +438,4 @@ for (const id of ids) {
435
438
  - 삭제·복구·이력 적재는 한 `connectAsync` 트랜잭션 안에서 수행 — 데이터만 바뀌고 이력이 빠지거나 그 반대가 되지 않게 함.
436
439
  - 벌크 복구는 하나라도 충돌하면 전체 롤백(원자성). 충돌분만 빼고 나머지를 복구하지 않음.
437
440
  - 활성 유니크 검증은 복구 경로에서 빠뜨리지 않음 — 단건은 선검증, 벌크는 후검증. 활성 유니크가 없는 모델이면 생략 가능.
438
- - 단건(detail)은 삭제 후 [client-component.md "detail 데이터 흐름"](./client-component.md) 의 계약대로 `submitted.emit(true)` 로 부모에 통지(modal 컨텍스트에선 모달 호스트가 위에 `close` 닫음 `emit` 대체 아님). 복구 후엔 닫지 않고 refresh — 복구 직후 상세를 계속 보도록.
441
+ - 단건(detail)은 삭제 후 부모에 통지 임베드(컨트롤) `submitted.emit(true)`, 모달이면 `close.emit(payload)`결과 반환(호출 측이 `showAsync` 반환으로 refresh). output 독립이며 사용 맥락에 따라 한쪽 또는 양쪽 ([client-component.md "detail 데이터 흐름"](./client-component.md) 참조). 복구 후엔 닫지 않고 refresh — 복구 직후 상세를 계속 보도록.
@@ -7,7 +7,7 @@
7
7
 
8
8
  ## AppOrmProvider 를 정의하려면 (새 앱 1회성)
9
9
 
10
- 앱의 DbContext·DB명·스키마를 한 곳에 고정하고, 트랜잭션 유무별 진입 메서드를 제공.
10
+ 앱의 DbContext·DB명·스키마를 한 곳에 고정하고, `connectAsync` 메서드로 쿼리를 실행.
11
11
 
12
12
  ```ts
13
13
  @Injectable({ providedIn: "root" })
@@ -24,13 +24,6 @@ export class AppOrmProvider {
24
24
  callback,
25
25
  );
26
26
  }
27
-
28
- connectWithoutTransAsync<R>(callback: (db: MainDbContext) => Promise<R>): Promise<R> {
29
- return this._appService.orm.connectWithoutTransaction(
30
- { /* 같은 옵션 */ },
31
- callback,
32
- );
33
- }
34
27
  }
35
28
  ```
36
29
 
@@ -38,7 +31,7 @@ export class AppOrmProvider {
38
31
 
39
32
  - `@Injectable({ providedIn: "root" })`.
40
33
  - DbContext 는 앱별로 정의 (예: `@adtek/db-main` 의 `MainDbContext`). 스키마 정의는 [orm.md](./orm.md).
41
- - 기본 메서드는 `connectAsync` (트랜잭션 포함). `connectWithoutTransAsync` 는 initialize 등 트랜잭션 안에서 동작하지 않는 작업 전용 헬퍼.
34
+ - 진입 메서드는 `connectAsync` (트랜잭션 포함).
42
35
  - 콜백의 반환값이 그대로 메서드의 반환값이 됨.
43
36
 
44
37
  ## 화면·프로바이더에서 쿼리를 실행하려면
@@ -54,9 +47,7 @@ const rows = await this._appOrm.connectAsync(async (db) => {
54
47
  ```
55
48
 
56
49
  - 콜백 인자 `db` 는 `MainDbContext`. 테이블·뷰 빌더와 쿼리 작성은 [orm.md](./orm.md).
57
- - 트랜잭션이 곤란한 작업(initialize 등)만 `connectWithoutTransAsync` 사용.
58
50
 
59
51
  ## 지킬 것
60
52
 
61
- - DB 옵션(`DbClass`·`connOpt`·`dbContextOpt`)은 `AppOrmProvider` 한 곳에만 두고, 화면·프로바이더는 `connectAsync`/`connectWithoutTransAsync` 만 호출. 옵션을 호출부에 흩뿌리지 않음.
62
- - 기본은 `connectAsync`. 트랜잭션 없이 돌려야 하는 명확한 이유가 있을 때만 `connectWithoutTransAsync`.
53
+ - DB 옵션(`DbClass`·`connOpt`·`dbContextOpt`)은 `AppOrmProvider` 한 곳에만 두고, 화면·프로바이더는 `connectAsync` 만 호출. 옵션을 호출부에 흩뿌리지 않음.
@@ -12,12 +12,14 @@ ORM 사용은 [client-orm.md](./client-orm.md), 이벤트 정의·발생 메커
12
12
  서버 연결·서비스·이벤트·ORM 진입점을 한 root provider 에 모음. 서비스·이벤트는 `private _xxx?` 캐시 필드 + getter 로 lazy 노출(`??=`).
13
13
 
14
14
  ```ts
15
+ export const APP_MAIN_SERVICE_KEY = "MAIN";
16
+
15
17
  @Injectable({ providedIn: "root" })
16
18
  export class AppServiceProvider {
17
19
  private readonly _sdServiceClientFactory = inject(SdServiceClientFactoryProvider);
18
20
 
19
21
  get client() {
20
- return this._sdServiceClientFactory.get("MAIN");
22
+ return this._sdServiceClientFactory.get(APP_MAIN_SERVICE_KEY);
21
23
  }
22
24
 
23
25
  private _orm?: OrmClientConnector;
@@ -30,13 +32,22 @@ export class AppServiceProvider {
30
32
  return (this._user ??= this.client.getService<UserServiceMethods>("User"));
31
33
  }
32
34
 
33
- private _authInfoEvent?: ClientEventProxy<typeof AuthInfoEvent>;
34
- get authInfoEvent(): ClientEventProxy<typeof AuthInfoEvent> {
35
- return (this._authInfoEvent ??= this.client.getEvent(AuthInfoEvent));
35
+ private _authInfoChangedEvent?: ClientEventProxy<typeof AuthInfoChangedEvent>;
36
+ get authInfoChangedEvent(): ClientEventProxy<typeof AuthInfoChangedEvent> {
37
+ return (this._authInfoChangedEvent ??= this.client.getEvent(AuthInfoChangedEvent));
36
38
  }
37
39
 
38
40
  async connectAsync() {
39
- await this._sdServiceClientFactory.connectAsync("MAIN");
41
+ await this._sdServiceClientFactory.connectAsync(
42
+ APP_MAIN_SERVICE_KEY,
43
+ Boolean(env("SERVER_HOST"))
44
+ ? {
45
+ host: env("SERVER_HOST"),
46
+ port: num.parseInt(env("SERVER_PORT")),
47
+ ssl: parseBoolEnv(env("SERVER_SSL")),
48
+ }
49
+ : {},
50
+ );
40
51
  }
41
52
  }
42
53
  ```
@@ -44,9 +55,9 @@ export class AppServiceProvider {
44
55
  **약속**:
45
56
 
46
57
  - `@Injectable({ providedIn: "root" })`.
47
- - `client` getter — `SdServiceClientFactoryProvider.get("MAIN")` 결과. 서비스·이벤트·ORM 의 공통 진입점.
58
+ - `client` getter — `SdServiceClientFactoryProvider.get(APP_MAIN_SERVICE_KEY)` 결과. 서비스·이벤트·ORM 의 공통 진입점. 서비스 키(`"MAIN"`)는 `client.get`·`connectAsync` 등 여러 곳에서 참조하므로 상수로 추출.
48
59
  - `orm` getter — `createOrmClientConnector(this.client)` 결과. DB 설정을 얹는 `AppOrmProvider` 가 이 위에 올라감 ([client-orm.md](./client-orm.md)).
49
- - `connectAsync()` — 앱 부트스트랩 시점에 서버 연결 수행. `addListener` 등 통신은 이 호출 이후에만 가능.
60
+ - `connectAsync()` — 앱 부트스트랩 시점에 서버 연결 수행. 클라이언트·서버를 다른 호스트로 배포할 때를 위해 env(`SERVER_HOST`·`SERVER_PORT`·`SERVER_SSL`)로 연결 옵션을 주입(미설정이면 same-origin). `env`·`num`·`parseBoolEnv` 는 `@simplysm/core-common`. `addListener` 등 통신은 이 호출 이후에만 가능.
50
61
 
51
62
  ## 부트스트랩에서 서버에 연결하려면
52
63
 
@@ -12,10 +12,10 @@
12
12
 
13
13
  ```ts
14
14
  export function useSharedSignal<K extends keyof TAppSharedData>(
15
- name: K,
15
+ dataKey: K,
16
16
  ): SharedDataHandle<TAppSharedData[K]> {
17
17
  const appSharedData = inject(AppSharedDataProvider);
18
- return appSharedData.getHandle(name);
18
+ return appSharedData.getHandle(dataKey);
19
19
  }
20
20
 
21
21
  @Injectable({ providedIn: "root" })
@@ -65,7 +65,23 @@ export interface ISharedCustomer extends SharedDataBase<number> {
65
65
 
66
66
  - `@Injectable({ providedIn: "root" })` 사용, `SdSharedDataProvider<TAppSharedData>` 를 상속.
67
67
  - 등록은 `override initialize()` 안에서 `this.register(name, opts)` 호출로 수행.
68
- - `useSharedSignal<K>(name)` 헬퍼를 함께 export — 컴포넌트는 inject 없이 이름만으로 접근.
68
+ - `useSharedSignal<K>(dataKey)` 헬퍼를 함께 export — 컴포넌트는 inject 없이 이름만으로 접근.
69
+
70
+ ## 부트스트랩에 연결하려면 (새 앱 1회성)
71
+
72
+ 라이브러리 공유데이터 컨트롤(`sd-shared-data-select` · `sd-shared-data-select-list`)은 base 토큰 `SdSharedDataProvider` 를 inject 하므로, 부트스트랩 providers 에 앱 provider 를 그 토큰의 별칭으로 등록.
73
+
74
+ ```ts
75
+ // 앱 부트스트랩 (main.ts)
76
+ bootstrapApplication(AppRoot, {
77
+ providers: [
78
+ // ...
79
+ { provide: SdSharedDataProvider, useExisting: AppSharedDataProvider },
80
+ ],
81
+ });
82
+ ```
83
+
84
+ - 이 별칭이 없으면 컨트롤이 데이터가 등록된 `AppSharedDataProvider` 가 아니라 빈 base 인스턴스를 잡아, 공유데이터 select 컨트롤에 항목이 표시되지 않음.
69
85
 
70
86
  ## 마스터 데이터 항목을 추가하려면
71
87
 
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: sd-spec
3
- description: SI/업무시스템 요구사항을 분석해 spec.md 로 구조화. 배치 작성(분석/설계 워크플로) + 부분 수정 인라인 처리. Use when "요구사항 분석", "SI 분석", "업무시스템 spec 작성" 을 요청할 때.
4
- model: xhigh
3
+ description: SI/업무시스템 요구사항을 분석해 spec.md 로 구조화. 초안 배치(요건→설계 순차 빌드) + 부분 수정 인라인 처리. Use when "요구사항 분석", "SI 분석", "업무시스템 spec 작성" 을 요청할 때.
5
4
  ---
6
5
 
7
6
  # sd-spec
@@ -18,43 +17,40 @@ model: xhigh
18
17
 
19
18
  ## 연산
20
19
 
21
- sd-spec 은 3종의 연산으로 동작. 사용자 지시를 받으면 아래 디스패치로 분기. per-unit "맞나요?" 확인 루프는 없음 — 배치로 한 번에 작성하고 사용자가 사후 검토(검토 패키지)로 정정.
20
+ sd-spec 은 2종의 연산으로 동작. 사용자 지시를 받으면 아래 디스패치로 분기. 작성은 배치로 한 번에 하고, 사용자가 사후 검토(검토 패키지)로 정정.
22
21
 
23
22
  ### 디스패치
24
23
 
25
24
  | 지시 유형 | 연산 | 처리 경로 |
26
25
  | ----------------------------------------------------- | ----------- | --------------------------------------------- |
27
- | "분석해줘"·"요구사항 분석"·"업무 프로세스 분석" 류 | 분석 배치 | Workflow 도구로 `workflow-analyze.js` 실행 |
28
- | "설계해줘"·"화면 설계"·"자동 처리 설계" | 설계 배치 | Workflow 도구로 `workflow-design.js` 실행 |
29
- | 그 외 모든 지시 ("X를 Y로"·"A를 B와 합쳐"·"이 섹션 이상해"·재진입 정정) | 부분 수정 | SKILL.md 인라인 처리 (아래 "부분 수정") |
26
+ | "분석해줘"·"요구사항 분석"·"초안 작성"·"spec 작성" 류 (신규 spec.md 작성) | 초안 배치 | 메인 에이전트가 직접 순차 수행 (아래 "초안 배치") |
27
+ | 그 외 모든 지시 ("X를 Y로"·"A를 B와 합쳐"·" 섹션 이상해"·"설계만 다시"·재진입 정정) | 부분 수정 | SKILL.md 인라인 처리 (아래 "부분 수정") |
30
28
 
31
- - 분석/설계 배치는 백그라운드 자율 실행. 실행 중 사용자에게 묻지 않음 (모르는 건 [OPEN]).
32
- - 재진입(기존 spec.md 첨부 추가 지시) 기본적으로 부분 수정 경로. 지시가 "다음 단계 전체를 배치로 진행"이면 해당 배치 연산.
29
+ - 초안 배치는 메인 에이전트가 자율 진행. 실행 중 사용자에게 묻지 않음 (모르는 건 [OPEN]).
30
+ - 초안 배치는 **신규 spec.md 작성 전용** (요건→설계를 한 흐름으로 통합). 기존 spec.md 있는 재진입(부분 재작업·정정·"설계만 다시")은 모두 부분 수정 경로.
33
31
 
34
- ### 분석 배치
32
+ ### 초안 배치 (메인 직접 순차 수행)
35
33
 
36
- "분석해줘" 류 지시. 메인 에이전트가 Workflow 도구를 호출해 분석 워크플로에 위임:
34
+ 신규 spec.md 작성 지시("분석해줘"·"초안 작성"·"spec 작성" ). 메인 에이전트가 spec.md **한 줄기로 순차 빌드**함 (코딩하듯 — 앞 단위가 만든 §7·§8·§9 를 뒤 단위가 재사용·확장). 작성은 전부 메인 직접, **검증만 독립 Agent**.
37
35
 
38
- - `Workflow({ scriptPath: "<이 스킬 폴더>/workflow-analyze.js", args: { specPath: "<spec.md 경로>", sourcePaths: ["<자료 경로>", ...], systemName: "<시스템명>", today: "<yyyy-MM-dd>", skillDir: "<이 스킬 폴더>" } })`.
39
- - `specPath`: 산출 대상 spec.md 경로. 없으면 위 "입출력" 의 폴더 규칙으로 신규 경로 생성 후 전달.
40
- - `sourcePaths`: Requirement Source 자료 경로 목록 (사용자 첨부·발화 기록 포함).
41
- - `systemName`: 사용자와 합의한 시스템명 (spec.md 골격의 `{name}`).
42
- - `today`: 현재 날짜 (PowerShell `Get-Date -Format "yyyy-MM-dd"`). §10 로그 타임스탬프용. 워크플로는 전역 현재시각을 쓰지 않으므로 필요한 시각은 args 로 전달.
43
- - `skillDir` / `<이 스킬 폴더>`: 이 SKILL.md 가 위치한 디렉터리의 절대경로 — 에이전트가 런타임에 채움 (워크플로가 SKILL.md·example-spec.md 를 읽는 데 사용). 배포 환경마다 경로가 다르므로 하드코딩하지 말 것.
44
- - 워크플로가 §1~§3 (분석 명세) + 자연 도출 §7~§9 + §10 을 자율 작성해 spec.md 에 write.
45
- - 완료 후 메인 에이전트는 "연산 사이 검토" 의 검토 패키지를 제시하고 정지.
36
+ **준비**:
46
37
 
47
- ### 설계 배치
38
+ - 폴더·경로: "입출력" 의 폴더 규칙으로 신규 `.specs/{yyMMddHHmmss}_{slug}/spec.md` 경로를 만들고, "spec.md 골격" 으로 골격(§1~§10 빈 헤더)을 Write.
39
+ - 시스템명: 사용자와 합의한 시스템명(제목 `{name}`). 날짜: PowerShell `Get-Date -Format "yyyy-MM-dd"` (§10 로그 타임스탬프용). 자료(Requirement Source)는 직접 Read.
48
40
 
49
- "설계해줘" 지시. 확정된 §1~§3 입력으로 설계 워크플로에 위임:
41
+ **단계 (직렬 단계는 시점에 확정된 것만 다룸)**:
50
42
 
51
- - `Workflow({ scriptPath: "<이 스킬 폴더>/workflow-design.js", args: { specPath: "<spec.md 경로>", skillDir: "<이 스킬 폴더>" } })` (설계는 확정 §1~3 입력 `sources`/`today` 불필요).
52
- - 워크플로가 §4 화면 / §5 자동 처리 / §6 공통·기반 기능 (+ 그 과정에서 도출되는 §7~§9 보강) 을 자율 작성해 spec.md 에 write.
53
- - 완료 검토 패키지 제시 정지.
43
+ 1. **요건 (§1~3 + §7)**: `references/format.md` 요건·공통 작성법(§1·§2·§3·§7) + 공유 형식("spec.md 형식" 절)을 Read. 자료를 직접 Read §1 개요·§2 업무 프로세스(pivotal event §2/§3 분할)·§3 기타 요구를 작성. 작성 중 나오는 도메인 어휘·공통 규격은 §7 에 누적. **§4~9 는 빈 헤더로 두고 손대지 않음** (설계가 아직임). 비자명 항목엔 근거, 모르면 [OPEN].
44
+
45
+ 2. **설계 (§4~6 순차 빌드 + §8·§9·§7 점진 누적)**: `references/format.md` 의 설계 작성법(§4~9)을 Read. §1~3·§7 을 입력으로 §4 화면 → §5 자동 처리 → §6 공통·기반 의 단위를 **하나씩 순차로** 작성. 각 단위(§4.x 등)를 작성하면서 그 단위가 참조하는 §8 도메인 모델·§9 외부 인터페이스를 **그 자리에서 즉시 spec 에 추가·갱신** (없으면 신설, 있으면 재사용·필드 확장)하고, 새 용어는 §7 에 누적. §4 첫머리 화면 목록 표도 단위가 늘 때마다 갱신. 별도 "집계" 단계는 없음 — 누적이 곧 §8·§9 작성임 (§8·§9 불변식은 "섹션 구조" 절 참조).
46
+
47
+ 3. **검증 (독립 Agent, 규모 비례)**: 작성 완료 spec 을, 작성한 메인이 아닌 **독립 서브에이전트(Agent 도구)로 검증**함 (자기검증 편향 차단). 검증 항목 — ① 근거 좌표 1:1 실재 대조(원자료 재독), ② 원자료↔spec 전수 누락 역대조 + 자료 커버리지, ③ 요구 커버리지(요건 → §4~6 역대조), ④ dangling 참조 grep. 규모(자료 수·근거 수)에 비례해 항목별/자료별로 Agent 를 병렬로 펼치되, 대상이 아주 작으면 메인 직접도 허용. 근거 위조로 판정된 항목은 `(근거: …)` 를 떼고 [OPEN] 으로 강등(원 좌표는 메모로 보존).
48
+
49
+ 4. **검토 패키지 + 정지**: "연산 사이 검토" 의 검토 패키지를 제시하고 정지.
54
50
 
55
51
  ### 부분 수정 (인라인)
56
52
 
57
- 분석/설계 배치 외의 모든 지시는 SKILL.md 가 메인 에이전트로 직접 처리. 워크플로 호출 없음. 재진입(기존 spec.md 에 대한 추가 작업)도 이 경로.
53
+ 초안 배치(신규 spec.md 작성) 외의 모든 지시는 SKILL.md 가 메인 에이전트로 직접 처리. 재진입(기존 spec.md 에 대한 추가 작업 — "설계만 다시"·재설계 포함)도 이 경로.
58
54
 
59
55
  - **정밀 지시** ("§4.2 의 X 컬럼을 Y로", "이 항목 [OPEN] 으로") → 바로 Edit. 무관 섹션은 건드리지 않음 (일괄 치환 금지 — Edit 의 `old_string` 을 유일 매칭되게 좁혀서 수정).
60
56
  - **모호 지적** ("이 섹션 이상해", "여기 흐름이 안 맞아") → 무엇이 문제인지 진단을 먼저 제시 → 사용자 합의 → Edit. 추측으로 바로 고치지 않음.
@@ -63,10 +59,6 @@ sd-spec 은 3종의 연산으로 동작. 사용자 지시를 받으면 아래
63
59
  - § 번호는 자주 바뀌므로 이름으로 추적. 변경 후 dangling 참조가 남지 않게 확인.
64
60
  - 작성 형식·신뢰도 표기는 아래 "작성 원칙" 과 **대상 섹션의 작성법** (아래 "§별 작성법" 절 표에서 해당 ref 를 Read) 을 따름.
65
61
 
66
- ### 워크플로 부재 시
67
-
68
- `disableWorkflows` 등으로 Workflow 도구를 쓸 수 없으면, 분석/설계 배치는 fallback 없이 "이 연산은 workflow 가 필요합니다 (현재 비활성)" 라고 명확히 안내하고 정지. 부분 수정은 워크플로 없이 정상 동작.
69
-
70
62
  ### 연산 사이 검토
71
63
 
72
64
  각 연산(배치·부분 수정) 완료 후 동일하게:
@@ -75,22 +67,21 @@ sd-spec 은 3종의 연산으로 동작. 사용자 지시를 받으면 아래
75
67
  2. **검토 패키지** 를 채팅으로 제시:
76
68
  - 이번에 쓴 섹션의 한 줄 요약.
77
69
  - `[OPEN]` 목록 (이번 연산이 남긴 미확정 항목).
78
- - verify 결과 요약 (배치 연산의 경우 워크플로 verify 단계가 반환한 근거 위조·누락·커버리지·dangling 참조 보고).
70
+ - 검증 결과 요약 (배치 연산의 경우 검증 Agent 반환한 근거 위조·누락·dangling 참조 보고).
79
71
  - 자료 커버리지 보고 (쓴 자료 중 spec 에 미반영된 부분).
80
- - **워크플로 에러 시** (배치): 분석·설계 워크플로는 fail-fast — 에이전트(자료 추출·섹션 작성·검증 등)가 하나라도 실패하면 부분 결과 없이 throw 함. 배치가 에러로 끝나면 검토 패키지를 만들지 말고 실패 사실을 [error] 로 알린 뒤 resume 재실행. 정상 완료 시에만 검토 패키지를 제시.
81
- 3. **정지**. 자동 진행하지 않음 분석이 끝나도 설계로 자동 진입 금지. 다음 연산은 사용자가 호출.
72
+ - **검증 Agent 실패 시** (배치): 검증 서브에이전트가 하나라도 실패하면 부분 결과로 검토 패키지를 만들지 말고 실패 사실을 [error] 로 알린 뒤 해당 검증만 재실행해 보완. 전건 정상 반환이어야 검증 완료로 간주.
73
+ 3. **정지**. 초안 배치가 끝나도 후속 수정으로 자동 진입 금지. 다음 연산은 사용자가 호출.
82
74
 
83
75
  ## 작성 원칙
84
76
 
85
77
  배치·부분 수정 모두에 적용되는 spec 정확성 원칙 (상위 행동 규칙·sd-design-rules 와 중복 금지, sd-spec 고유분만).
86
78
 
87
79
  - **비자명 항목 inline 근거** — 자료에서 해석·도출한 비자명 항목에는 근거를 부착(아래 "신뢰도 표기"). 사용자가 직접 말한 자명한 항목엔 붙이지 않음.
88
- - **모르면 [OPEN]** — 근거가 없으면 임의로 채우지 말고 [OPEN] 으로 표기. As-Is (회의록·매뉴얼·현행 화면) 근거인 항목·답변 범위 흡수는 근거 없음 → [OPEN].
89
- - **결측 보존**`""`·`0`·`false`·임의값으로 치환 금지. nullable nullable 로.
90
- - **단순화 차단** — spec 명시 정의·식·분기·경계를 자체 단순화·근사화·방어 처리(NULL 강제·0 클램프·가드·분기 생략) 로 임의 변형하지 않음. 식은 그대로 풀어쓰기. 정확 풀이 부담이 크면 단순화안을 [OPEN] 으로 표기하거나 검토 패키지에서 사용자에게 보고.
80
+ - **모르면 [OPEN]** — 근거가 없으면 임의로 채우지 말고 [OPEN] 으로 표기 (판정 기준은 "신뢰도 표기" 절).
81
+ - **단순화 차단**spec 의 식·분기·경계를 그대로 풀어쓰기. 방어 처리(NULL 강제·0 클램프·가드·분기 생략)로 임의 변형 금지(상위 "인터페이스 설계 시"·"결측 보존"의 spec 적용). 정확 풀이 부담이 크면 단순화안을 [OPEN] 표기하거나 검토 패키지에서 보고.
91
82
  - **형식 준수** — 각 § 작성법 규약 + 그 § 첫머리 "모범" 의 example-spec.md 모범 §를 형식 모범으로 직접 대조 (모범 Read 없이 텍스트 규칙만으로 작성 금지). 다른 §·example 에서는 **형식만** 가져오고 **내용물**(항목·컬럼·필드·액션·동작) 은 본 §의 자료·발언에서 직접 도출.
92
83
 
93
- 워크플로의 verify 단계는 위 원칙의 준수를 독립 컨텍스트에서 사후 검증(근거 좌표 1:1 대조·원자료↔spec 전수 누락 대조·요구목록 역대조·dangling 참조 grep). 사람 사후 검토(검토 패키지)가 최종 안전망.
84
+ 검증 단계(독립 Agent)는 위 원칙의 준수를 독립 컨텍스트에서 사후 검증함 (검증 항목은 "초안 배치" 3단계 참조). 사람 사후 검토(검토 패키지)가 최종 안전망.
94
85
 
95
86
  ## spec.md 형식
96
87
 
@@ -98,17 +89,25 @@ sd-spec 은 3종의 연산으로 동작. 사용자 지시를 받으면 아래
98
89
 
99
90
  spec.md 의 § 별 섹션 이름과 분류:
100
91
 
92
+ **분류 기준**:
93
+
94
+ - **요건**: 고객 입장 As-Is/To-Be — 무엇을·왜.
95
+ - **설계**: 그걸 어떻게 만드나 — 구현 + 구현에 필요한 정보(UI·데이터구조·외부API).
96
+ - **공통**: 요건·설계 양쪽에서 도출·편집되는 어휘·규격.
97
+ - **자연 도출**: 직접 작성하는 단위가 아니라 다른 섹션을 쓰다 파생되는 섹션. 출처가 공통(§7 — 양쪽)인지 설계(§8·§9 — §4~6 파생)인지로 구분.
98
+ - **§8·§9 불변식**: §4~6 에 없는 기능의 도메인 모델·외부 인터페이스는 존재할 수 없음 — 설계 산물로서 §4~6 의 종속.
99
+
101
100
  | § | 섹션 | 분류 |
102
101
  | --- | ---------------- | ---------------------------- |
103
- | §1 | 개요 | 분석 명세 |
104
- | §2 | 업무 프로세스 | 분석 |
105
- | §3 | 기타 요구사항 | 분석 |
102
+ | §1 | 개요 | 요건 명세 |
103
+ | §2 | 업무 프로세스 | 요건 |
104
+ | §3 | 기타 요구사항 | 요건 |
106
105
  | §4 | 화면 | 설계 |
107
106
  | §5 | 자동 처리 | 설계 |
108
107
  | §6 | 공통·기반 기능 | 설계 |
109
- | §7 | 공통 정의 | 자연 도출 (다른 섹션 반영거리) |
110
- | §8 | 도메인 모델 | 자연 도출 (다른 섹션 반영거리) |
111
- | §9 | 외부 인터페이스 | 자연 도출 (다른 섹션 반영거리) |
108
+ | §7 | 공통 정의 | 공통 자연 도출 (요건·설계 양쪽에서 도출·편집) |
109
+ | §8 | 도메인 모델 | 설계 자연 도출 (§4~6 에서 파생) |
110
+ | §9 | 외부 인터페이스 | 설계 자연 도출 (§4~6 에서 파생) |
112
111
  | §10 | 본문 외 확정 사항 | 메타 (결정이 발생할 때마다 누적) |
113
112
 
114
113
  ### 신뢰도 표기
@@ -123,7 +122,7 @@ spec.md 본문에서 항목의 신뢰도를 나타내는 표기. 전부 날짜
123
122
  | `[구현]` | sd-impl 소관. sd-spec 은 존재만 인지, 부착·제거하지 않음. |
124
123
 
125
124
  - `[OPEN]` 판정: 근거 부족(상위 행동 규칙 "결정 근거" 적용). As-Is 만 근거 / 추정 / 답변 범위 흡수 = 근거 없음 → `[OPEN]`.
126
- - `[확정]` 마커는 없음. 확정 여부는 사용자가 필요 시 직접 관리 (헤더에 `[확정: 날짜]`·`[OPEN: 날짜]` 등의 상태 마커를 붙이지 않음).
125
+ - 확정 여부는 사용자가 직접 관리 헤더에 상태 마커(`[확정: 날짜]`·`[OPEN: 날짜]` 등)를 붙이지 않음.
127
126
  - 예외: §10 "본문 외 확정 사항" 의 로그 항목은 순서 보존용 날짜를 유지(`- YYYY-MM-DD: ...`). 이건 신뢰도 마커가 아니라 로그 타임스탬프.
128
127
 
129
128
  ### 본문 내 참조 (이름 기반)
@@ -164,16 +163,11 @@ spec.md 본문에서 항목의 신뢰도를 나타내는 표기. 전부 날짜
164
163
 
165
164
  ## §별 작성법
166
165
 
167
- 각 § 의 작성법(분할 절차·본문 구조·sub-section·모범)은 단계별 reference 분리됨. 작성·부분 수정 대상 § 맞는 파일을 Read:
168
-
169
- | 대상 § | 작성법 위치 |
170
- | ----------------------------------------------------------------------------------- | ----------------------------- |
171
- | §1 개요 · §2 업무 프로세스 · §3 기타 요구사항 · §7 공통 정의 · §8 도메인 모델 · §9 외부 인터페이스 | `references/format-analyze.md` |
172
- | §4 화면 · §5 자동 처리 · §6 공통·기반 기능 | `references/format-design.md` |
166
+ 각 § 의 작성법(분할 절차·본문 구조·sub-section·모범)은 `references/format.md` 있음 요건·공통(§1·§2·§3·§7)과 설계(§4·§5·§6·§8·§9)를 파일로 통합. 작성·부분 수정 Read:
173
167
 
174
168
  - 공유 형식(위 "spec.md 형식" 절: 섹션 구조·신뢰도 표기·본문 내 참조·sub-section 헤더 레벨·진행 방법)·골격·§10 은 본 SKILL.md 에 유지.
175
- - **부분 수정** 시: 편집 대상 섹션이 위 표의 어느 파일에 속하는지 보고 그 파일의 작성법을 Read 한 뒤 편집.
176
- - **배치**워크플로(analyze/design)가 해당 ref 를 Read.
169
+ - **부분 수정** 시: `references/format.md` 의 편집 대상 섹션 작성법을 Read 한 뒤 편집.
170
+ - **초안 배치** 메인이 요건 단계·설계 단계에서 `references/format.md` 를 Read.
177
171
 
178
172
  ## §10 본문 외 확정 사항
179
173
 
@@ -187,7 +181,7 @@ spec.md 본문에서 항목의 신뢰도를 나타내는 표기. 전부 날짜
187
181
 
188
182
  - **헤더 1줄** (필수): `- YYYY-MM-DD: <결정 한 줄>`.
189
183
  - 분석 제외 시: `- YYYY-MM-DD [제외]: <결정 한 줄>`.
190
- - 날짜는 args 전달받은 `today` (전역 현재시각 사용 금지). 순서 보존용 로그 타임스탬프이며 신뢰도 마커가 아님.
184
+ - 날짜는 "초안 배치" 준비 단계에서 PowerShell `Get-Date` 생성한 값. 순서 보존용 로그 타임스탬프이며 신뢰도 마커가 아님.
191
185
  - **자식 bullet**: 형식 자유 (개수·내용 모두 자유).
192
186
  - **결정 근거 1개 이상 필수**.
193
187
  - 자유 항목 예시: 자료 위치·영향 범위·대화 발췌·메모·관련 § 등.
@@ -223,9 +217,9 @@ spec.md 본문에서 항목의 신뢰도를 나타내는 표기. 전부 날짜
223
217
  - 근거: 사용자 합의 — 와이어 분량 관리
224
218
  ```
225
219
 
226
- ## spec.md 골격 (분석 배치)
220
+ ## spec.md 골격 (초안 배치)
227
221
 
228
- 분석 배치에서 spec.md 를 신규 생성할 때 사용하는 골격. `{name}` 은 사용자와 합의한 시스템명 (args 의 `systemName`).
222
+ 초안 배치에서 spec.md 를 신규 생성할 때 사용하는 골격. `{name}` 은 사용자와 합의한 시스템명.
229
223
 
230
224
  ```markdown
231
225
  # {name} 요구 분석서
@@ -259,18 +253,15 @@ spec.md 본문에서 항목의 신뢰도를 나타내는 표기. 전부 날짜
259
253
  ## 10. 본문 외 확정 사항
260
254
  ```
261
255
 
262
- 분석 배치 작성 범위:
263
- - §2.x/§3.x 헤더 + 본문 (BPMN·흐름 설명 등) 작성. 미확정 항목은 인라인 `[OPEN]`.
264
- - §1 개요 + 자연 도출되는 §7~§9 함께 작성.
265
- - §10 에는 분석 배치 중 자료에서 도출된 제외 결정 (`[제외]` 마커) 을 반영.
256
+ 초안 배치 작성 범위 (골격은 요건 단계에서 생성, 본문은 두 단계로 채워짐):
257
+ - 요건 단계: 골격(§1~§10 헤더) 생성 + §1 개요·§2.x/§3.x 헤더·본문 (BPMN·흐름 설명 등)·§7 공통 정의(공통 자연 도출) 작성. 미확정 항목은 인라인 `[OPEN]`. §4~6·§8·§9 는 헤더만 두고 본문은 비움.
258
+ - 설계 단계: §4 화면·§5 자동 처리·§6 공통·기반 기능 본문 작성 → 그 §4~6 이 참조한 모델·외부호출을 모아 §8 도메인 모델·§9 외부 인터페이스를 파생 작성.
259
+ - §10 에는 자료에서 도출된 제외 결정 (`[제외]` 마커) 을 반영.
266
260
 
267
261
  ## 외부 참조
268
262
 
269
263
  | 자료 | 용도 |
270
264
  | ---------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
271
- | [references/format-analyze.md](references/format-analyze.md) | 분석 작성법 (§1·§2·§3·§7·§8·§9). 분석 배치·§1~3·§7~9 부분 수정에서 Read |
272
- | [references/format-design.md](references/format-design.md) | 설계 작성법 (§4·§5·§6). 설계 배치·§4~6 부분 수정에서 Read |
273
- | [references/example-spec.md](references/example-spec.md) | spec.md 형식 모범 (WMS 예시 1건). 각 § 작성법의 "모범" 표기에서 참조 |
274
- | [workflow-analyze.js](workflow-analyze.js) | 분석 배치 워크플로 (map→reduce→verify). 분석 연산에서 Workflow 도구로 실행 |
275
- | [workflow-design.js](workflow-design.js) | 설계 배치 워크플로 (map→reduce→verify). 설계 연산에서 Workflow 도구로 실행 |
265
+ | [references/format.md](references/format.md) | §별 작성법 — 요건·공통 (§1·§2·§3·§7) + 설계 (§4·§5·§6·§8·§9). 초안 배치 요건·설계 단계 및 §1~9 부분 수정에서 Read |
266
+ | [references/example-spec.md](references/example-spec.md) | spec.md 형식 모범 (WMS 예시 1건). § 작성법의 "모범" 표기에서 참조 |
276
267
  | [.claude/references/sd-requirement-source-handling.md](../../references/sd-requirement-source-handling.md) | Requirement Source 부정확성 처리 (STT 오타·화자 모호·발화 모호·도메인 용어 다의성) |