@simplysm/sd-claude 14.0.91 → 14.0.93

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.
Files changed (93) hide show
  1. package/claude/references/sd-simplysm14/README.md +7 -6
  2. package/claude/references/sd-simplysm14/apis/angular/README.md +59 -39
  3. package/claude/references/sd-simplysm14/apis/angular/controls.md +119 -186
  4. package/claude/references/sd-simplysm14/apis/angular/crud.md +70 -31
  5. package/claude/references/sd-simplysm14/apis/angular/directives.md +55 -57
  6. package/claude/references/sd-simplysm14/apis/angular/features.md +86 -105
  7. package/claude/references/sd-simplysm14/apis/angular/infra.md +48 -57
  8. package/claude/references/sd-simplysm14/apis/angular/layout.md +37 -47
  9. package/claude/references/sd-simplysm14/apis/angular/overlay.md +82 -74
  10. package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +61 -50
  11. package/claude/references/sd-simplysm14/apis/angular/shared-data.md +74 -57
  12. package/claude/references/sd-simplysm14/apis/angular/sheet.md +63 -72
  13. package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +23 -18
  14. package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +21 -19
  15. package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +23 -18
  16. package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +72 -32
  17. package/claude/references/sd-simplysm14/apis/core-browser/README.md +18 -18
  18. package/claude/references/sd-simplysm14/apis/core-browser/dom-element.md +29 -29
  19. package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +41 -41
  20. package/claude/references/sd-simplysm14/apis/core-common/README.md +97 -90
  21. package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +75 -51
  22. package/claude/references/sd-simplysm14/apis/core-common/collection-ext.md +81 -0
  23. package/claude/references/sd-simplysm14/apis/core-common/errors.md +27 -29
  24. package/claude/references/sd-simplysm14/apis/core-common/obj.md +44 -45
  25. package/claude/references/sd-simplysm14/apis/core-common/serialization.md +34 -33
  26. package/claude/references/sd-simplysm14/apis/core-common/value-types.md +86 -0
  27. package/claude/references/sd-simplysm14/apis/core-node/README.md +6 -6
  28. package/claude/references/sd-simplysm14/apis/core-node/consola.md +3 -0
  29. package/claude/references/sd-simplysm14/apis/core-node/cpx.md +2 -2
  30. package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +1 -1
  31. package/claude/references/sd-simplysm14/apis/core-node/fsx.md +2 -2
  32. package/claude/references/sd-simplysm14/apis/core-node/worker.md +6 -3
  33. package/claude/references/sd-simplysm14/apis/excel/README.md +10 -10
  34. package/claude/references/sd-simplysm14/apis/excel/conditional-format.md +4 -2
  35. package/claude/references/sd-simplysm14/apis/excel/utils.md +1 -1
  36. package/claude/references/sd-simplysm14/apis/excel/workbook-worksheet.md +6 -6
  37. package/claude/references/sd-simplysm14/apis/lint/README.md +6 -32
  38. package/claude/references/sd-simplysm14/apis/lint/recommended.md +60 -0
  39. package/claude/references/sd-simplysm14/apis/lint/rules.md +17 -17
  40. package/claude/references/sd-simplysm14/apis/orm-common/README.md +15 -6
  41. package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +68 -102
  42. package/claude/references/sd-simplysm14/apis/orm-common/expr.md +75 -89
  43. package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +87 -99
  44. package/claude/references/sd-simplysm14/apis/orm-common/schema.md +110 -147
  45. package/claude/references/sd-simplysm14/apis/orm-common/types.md +48 -51
  46. package/claude/references/sd-simplysm14/apis/orm-node/README.md +8 -13
  47. package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +5 -5
  48. package/claude/references/sd-simplysm14/apis/sd-cli/README.md +9 -6
  49. package/claude/references/sd-simplysm14/apis/sd-cli/SdTsCompiler.md +9 -8
  50. package/claude/references/sd-simplysm14/apis/sd-cli/sd-config-types.md +23 -19
  51. package/claude/references/sd-simplysm14/apis/service-client/README.md +20 -12
  52. package/claude/references/sd-simplysm14/apis/service-client/orm.md +6 -6
  53. package/claude/references/sd-simplysm14/apis/service-client/transport.md +1 -1
  54. package/claude/references/sd-simplysm14/apis/service-common/README.md +35 -32
  55. package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +23 -22
  56. package/claude/references/sd-simplysm14/apis/service-common/protocol.md +23 -23
  57. package/claude/references/sd-simplysm14/apis/service-server/README.md +51 -43
  58. package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +6 -6
  59. package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +31 -21
  60. package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +8 -8
  61. package/claude/references/sd-simplysm14/apis/storage/README.md +55 -49
  62. package/claude/references/sd-simplysm14/manuals/client-component.md +843 -740
  63. package/claude/references/sd-simplysm14/manuals/client-crud.md +8 -0
  64. package/claude/references/sd-simplysm14/manuals/client-demo.md +6 -16
  65. package/claude/references/sd-simplysm14/manuals/client-shared-data.md +26 -0
  66. package/claude/references/sd-simplysm14/manuals/logging.md +1 -1
  67. package/claude/references/sd-simplysm14/manuals/orm.md +15 -1
  68. package/claude/rules/sd-design-rules.md +7 -0
  69. package/claude/sd-system-prompt.md +5 -8
  70. package/claude/skills/sd-debug/SKILL.md +43 -0
  71. package/claude/skills/sd-debug/workflow.js +390 -0
  72. package/claude/skills/sd-demo/SKILL.md +18 -20
  73. package/claude/skills/sd-dev/SKILL.md +127 -24
  74. package/claude/skills/sd-docs/SKILL.md +5 -3
  75. package/claude/skills/sd-docs/references/subagent-prompt.md +2 -3
  76. package/claude/skills/sd-impl/SKILL.md +18 -18
  77. package/claude/skills/sd-manual/SKILL.md +1 -0
  78. package/claude/skills/sd-review/SKILL.md +24 -18
  79. package/claude/skills/sd-review/workflow.js +324 -0
  80. package/claude/skills/sd-spec/SKILL.md +96 -679
  81. package/claude/skills/sd-spec/references/example-spec.md +28 -50
  82. package/claude/skills/sd-spec/references/format-analyze.md +232 -0
  83. package/claude/skills/sd-spec/references/format-design.md +248 -0
  84. package/claude/skills/sd-spec/workflow-analyze.js +615 -0
  85. package/claude/skills/sd-spec/workflow-design.js +667 -0
  86. package/claude/skills/sd-unpack/scripts/handlers/office_com.py +5 -1
  87. package/package.json +1 -1
  88. package/scripts/postinstall.mjs +157 -18
  89. package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +0 -68
  90. package/claude/references/sd-simplysm14/apis/core-common/array-ext.md +0 -77
  91. package/claude/references/sd-simplysm14/apis/core-common/datetime.md +0 -86
  92. package/claude/skills/sd-skill/SKILL.md +0 -245
  93. package/claude/skills/sd-skill/scripts/run_eval.py +0 -380
@@ -0,0 +1,667 @@
1
+ export const meta = {
2
+ name: "sd-spec-design",
3
+ description:
4
+ "확정된 §1~3 을 입력으로 §4 화면·§5 자동 처리·§6 공통·기반 기능을 도출·작성하고, 교차참조 정합·역대조 검증 후 spec.md 에 반영하는 설계 배치",
5
+ phases: [
6
+ { title: "Read", detail: "spec.md + format-design.md §별 작성법 로딩, 확정 §1~3(+§7~9) 추출" },
7
+ { title: "Derive", detail: "§4/§5/§6 헤더 분할 도출 (단일 에이전트)" },
8
+ { title: "Map", detail: "§4 화면 / §5 자동 처리 / §6 공통·기반 본문 병렬 작성" },
9
+ { title: "Reduce", detail: "화면 목록 표·모달/시트 재활용 교차참조 정합 (단일 에이전트)" },
10
+ { title: "Verify", detail: "근거 1:1 대조 · §1~3 요구목록→§4~6 역대조 · dangling 참조 · 커버리지 (병렬)" },
11
+ { title: "Write", detail: "§4~6 본문을 spec.md 에 반영" },
12
+ ],
13
+ };
14
+
15
+ // ── 입력 ───────────────────────────────────────────────────────
16
+ // args: spec.md 경로 문자열. (객체 {specPath} 도 허용)
17
+ if (args == null || (typeof args === "string" && args.trim() === "")) {
18
+ throw new Error("설계 대상 spec.md 경로를 args 로 전달하세요.");
19
+ }
20
+ const SPEC_PATH =
21
+ typeof args === "string"
22
+ ? args.trim()
23
+ : (args.specPath ?? args.spec ?? args.path ?? "").toString().trim();
24
+ if (SPEC_PATH === "") throw new Error("args 에서 spec.md 경로를 찾지 못했습니다.");
25
+
26
+ const SKILL_DIR = String((typeof args === "object" && args && args.skillDir) || "").replace(/[\/\\]+$/, "");
27
+ if (SKILL_DIR === "") throw new Error("args.skillDir (이 스킬 폴더 절대경로) 가 필요합니다.");
28
+ const SKILL_PATH = `${SKILL_DIR}/SKILL.md`;
29
+ const EXAMPLE_PATH = `${SKILL_DIR}/references/example-spec.md`;
30
+ const DESIGN_FMT = `${SKILL_DIR}/references/format-design.md`;
31
+ const ANALYZE_FMT = `${SKILL_DIR}/references/format-analyze.md`;
32
+
33
+ // ── 공통 원칙(모든 단계 주입) ──────────────────────────────────
34
+ // 설계 배치는 자율 실행 — 실행 중 사용자에게 묻지 않음. 모르는 건 [OPEN].
35
+ const PRINCIPLES = `
36
+ 대상 spec.md: ${SPEC_PATH}
37
+
38
+ 설계 배치 공통 원칙(자율·정확성):
39
+ - 자율 실행. 실행 중 사용자에게 질문하지 말 것. 결정할 근거가 없으면 임의로 채우지 말고 [OPEN] 으로 표기(필요 시 추측·필요자료 메모 동반).
40
+ - 결측 보존: "값 없음"(null/undefined/미상)을 ""/0/false/임의값으로 치환 금지.
41
+ - 단순화 차단: spec 명시 정의·식·분기·경계를 임의로 단순화·근사화·방어 처리(NULL 강제·0 클램프·가드)하지 말 것. 식은 그대로 풀어쓸 것.
42
+ - 불필요한 래핑·추상화 금지: 단순 입력은 그대로 전달.
43
+
44
+ 신뢰도 마커(전부 날짜 없음. 아래는 SKILL.md "신뢰도 표기" 절의 인라인 압축이며 충돌 시 SKILL.md 가 정본):
45
+ - (무표기): 직접·자명, 또는 미검토 초안. 기본 상태.
46
+ - 줄끝 \`(근거: 출처)\`: 자료에서 해석·도출한 **비자명** 항목에만 근거를 부착. 사용자가 직접 말한 자명한 항목엔 붙이지 않음. 표는 비고 칸, 산문은 줄 끝.
47
+ - \`[OPEN]\`: 근거 없음. As-Is(현행 화면·매뉴얼)만 근거이거나 답변 범위 흡수 = 근거 없음 → [OPEN]. 필요 시 추측·필요자료 메모 동반(예: \`[OPEN] A4 1매당 그리드 행/열 — 미확인\`).
48
+ - \`[구현]\`: sd-impl 소관. 존재만 인지, 부착·제거하지 않음.
49
+ - \`[확정]\` 마커는 폐기(사용하지 않음). 화면 헤더는 \`### N.N 화면명 (PC)\` / \`(PDA)\` 형식(장치 표기만 유지, 날짜 마커 없음).
50
+
51
+ 본문 내 참조(이름 기반. SKILL.md "본문 내 참조" 절의 인라인 압축이며 충돌 시 SKILL.md 가 정본): 다른 섹션은 § 번호 대신 \`[카테고리.이름]\` 형식으로 참조.
52
+ - 예: \`[모델.재고]\`·\`[화면.재고 확인]\`·\`[프로세스.입고]\`·\`[기타.과거 재고 조회]\`·\`[자동 처리.재고 스냅샷]\`·\`[기반.앱 구조 정의]\`·\`[외부 인터페이스.ERP 입고 통보]\`·\`[공통 정의.Location 라벨]\`.
53
+ - \`관련 섹션: [카테고리.이름], ...\` 한 줄로 참조·의존 섹션 나열.
54
+
55
+ 형식 권위: §4~6 작성법은 반드시 ${DESIGN_FMT} 를 Read 해 따른다(텍스트 규칙만으로 추정 금지). 공유 형식(섹션 구조·신뢰도 표기·본문 내 참조·sub-section 헤더 레벨)은 ${SKILL_PATH}, §7~9 를 보강할 때의 작성법은 ${ANALYZE_FMT}. 가장 가까운 모범은 ${EXAMPLE_PATH} 의 해당 § 를 Read 해 헤더 구조·표 형식을 직접 대조([구현] 마커는 sd-impl 소관이라 모방 금지).
56
+ - 설계 작성법(§4~6): ${DESIGN_FMT}
57
+ - 공유 형식·골격: ${SKILL_PATH}
58
+ - §7~9 작성법: ${ANALYZE_FMT}
59
+ - 모범(example-spec.md): ${EXAMPLE_PATH}
60
+ `;
61
+
62
+ // ── fail-fast 가드 ─────────────────────────────────────────────
63
+ // parallel 배리어 직후 호출. 결과에 null(에이전트 reject/스킵)이 하나라도 있으면
64
+ // 부분 결과로 진행하지 않고 즉시 throw. 정상이지만 빈 결과(빈 배열 등)는 null 이 아니라 통과.
65
+ function assertNoFailures(results, stage, labels) {
66
+ const failed = results.flatMap((r, i) => (r ? [] : [labels?.[i] ?? `#${i}`]));
67
+ if (failed.length > 0) {
68
+ throw new Error(
69
+ `[${stage}] 에이전트 ${failed.length}/${results.length}건 실행 실패(null) — 부분 결과로 진행 금지(fail-fast). 실패: ${failed.join(", ")}. resume 로 재실행하면 성공분은 캐시됩니다.`,
70
+ );
71
+ }
72
+ }
73
+
74
+ // ── 스키마 ─────────────────────────────────────────────────────
75
+ // Derive: §4/§5/§6 헤더 분할 결과 + 입력 컨텍스트(요구목록·도메인 모델 등) 요약.
76
+ const DERIVE_SCHEMA = {
77
+ type: "object",
78
+ additionalProperties: false,
79
+ required: ["screens", "autoProcesses", "commonBases", "requirements", "context", "notes"],
80
+ properties: {
81
+ screens: {
82
+ type: "array",
83
+ description: "§4 화면 도출 결과 (화면 목록 표의 행이 됨)",
84
+ items: {
85
+ type: "object",
86
+ additionalProperties: false,
87
+ required: ["name", "category", "kind", "device", "source"],
88
+ properties: {
89
+ name: { type: "string", description: "화면명 ([화면.X] 의 X). '(모달)' 등 사용 맥락 표기 금지" },
90
+ category: { type: "string", description: "도메인 묶음·메뉴 그룹 자유 명칭 (예: 기준정보·입고·재고)" },
91
+ kind: { type: "string", enum: ["마스터", "트랜잭션", "조회"], description: "유형" },
92
+ device: { type: "string", enum: ["PC", "PDA", "기타"], description: "장치" },
93
+ source: { type: "string", description: "도출 근거 (예: '[프로세스.입고] BPMN 액션 노드', '[모델.품목] 마스터', '[기타.과거 재고 조회]')" },
94
+ },
95
+ },
96
+ },
97
+ autoProcesses: {
98
+ type: "array",
99
+ description: "§5 자동 처리 도출 결과 (스케줄·이벤트 트리거가 명시적으로 있는 백그라운드 처리)",
100
+ items: {
101
+ type: "object",
102
+ additionalProperties: false,
103
+ required: ["name", "trigger", "source"],
104
+ properties: {
105
+ name: { type: "string", description: "자동 처리명 ([자동 처리.X] 의 X)" },
106
+ trigger: { type: "string", description: "스케줄·이벤트 트리거 (예: '매일 0시 정각')" },
107
+ source: { type: "string", description: "도출 근거 (§2/§3 본문 위치·인용)" },
108
+ },
109
+ },
110
+ },
111
+ commonBases: {
112
+ type: "array",
113
+ description: "§6 공통·기반 기능 도출 결과 (§4/§5 에 속하지 않는 개발 단위: 부수효과 동작 + 전역 정적 골격)",
114
+ items: {
115
+ type: "object",
116
+ additionalProperties: false,
117
+ required: ["name", "framework", "source"],
118
+ properties: {
119
+ name: { type: "string", description: "공통·기반 기능명 ([기반.X] 의 X)" },
120
+ framework: {
121
+ type: "boolean",
122
+ description: "프레임워크 기본 기능(매뉴얼 존재)이면 true → 참조 매뉴얼 stub. 앱 고유 bespoke 면 false → 본문 자유 서술",
123
+ },
124
+ source: { type: "string", description: "도출 근거 (§3 시스템 기반 류 요구 등)" },
125
+ },
126
+ },
127
+ },
128
+ requirements: {
129
+ type: "array",
130
+ description: "§1~3(+§7~9)에서 추출한 요구 항목 평면 목록 — Verify 의 §4~6 역대조 기준이 됨. BPMN 액션 노드·흐름 bullet 룰·§3 직접 요구·시스템 기반 요구를 빠짐없이 추출",
131
+ items: {
132
+ type: "object",
133
+ additionalProperties: false,
134
+ required: ["id", "text", "origin"],
135
+ properties: {
136
+ id: { type: "string", description: "요구 식별자 (예: R1, R2 …)" },
137
+ text: { type: "string", description: "요구 한 줄 (사용자 도메인 어휘)" },
138
+ origin: { type: "string", description: "출처 (§2.1 BPMN 노드 / §2.1 흐름 bullet / §3.1 / §7.x 등)" },
139
+ },
140
+ },
141
+ },
142
+ context: {
143
+ type: "string",
144
+ description: "§1~3(+§7~9) 핵심 요약 — map 단계 각 작성 에이전트가 공유할 도메인 컨텍스트(프로세스·도메인 모델·외부 자료·외부 인터페이스 핵심)",
145
+ },
146
+ notes: { type: "string", description: "도출 근거·애매점·[OPEN] 후보 요약" },
147
+ },
148
+ };
149
+
150
+ // 한 섹션 본문 작성 결과.
151
+ const SECTION_SCHEMA = {
152
+ type: "object",
153
+ additionalProperties: false,
154
+ required: ["section", "title", "markdown", "relatedRefs", "opens", "evidence"],
155
+ properties: {
156
+ section: { type: "string", description: "섹션 종류: 화면 | 자동 처리 | 기반" },
157
+ title: { type: "string", description: "이 단위명 ([화면.X]/[자동 처리.X]/[기반.X] 의 X)" },
158
+ markdown: { type: "string", description: "format-design.md §별 작성법을 따른 완성 본문 (헤더 §번호 제외, 헤더 텍스트부터). reduce 에서 §번호 부여·정합" },
159
+ relatedRefs: {
160
+ type: "array",
161
+ items: { type: "string" },
162
+ description: "이 본문에서 참조한 [카테고리.이름] 전부 (dangling 검증용)",
163
+ },
164
+ opens: {
165
+ type: "array",
166
+ items: { type: "string" },
167
+ description: "이 본문에 부착한 [OPEN] 항목들 (검토 패키지용)",
168
+ },
169
+ evidence: {
170
+ type: "array",
171
+ items: { type: "string" },
172
+ description: "이 본문에 부착한 (근거: 출처) 항목들 — '<항목>: <출처>' 형식 (verify 1:1 대조용)",
173
+ },
174
+ },
175
+ };
176
+
177
+ // Verify(역대조·근거·dangling·커버리지): 통합 검증 결과.
178
+ const VERIFY_SCHEMA = {
179
+ type: "object",
180
+ additionalProperties: false,
181
+ required: ["forgedEvidence", "uncovered", "danglingRefs", "coverageGaps", "verdict", "notes"],
182
+ properties: {
183
+ forgedEvidence: {
184
+ type: "array",
185
+ description: "(근거:…) 좌표가 원자료에서 실제 그 내용을 담지 않는 항목 — [OPEN] 강등 대상",
186
+ items: {
187
+ type: "object",
188
+ additionalProperties: false,
189
+ required: ["item", "claimedSource", "finding"],
190
+ properties: {
191
+ item: { type: "string", description: "근거가 붙은 spec 항목" },
192
+ claimedSource: { type: "string", description: "spec 이 주장한 출처 좌표" },
193
+ finding: { type: "string", description: "원자료 재확인 결과 (불일치 사유)" },
194
+ },
195
+ },
196
+ },
197
+ uncovered: {
198
+ type: "array",
199
+ description: "원자료↔spec 전수 대조로 발견한, §4~6 에 반영되지 않은 요구·자료 부분",
200
+ items: {
201
+ type: "object",
202
+ additionalProperties: false,
203
+ required: ["requirement", "origin", "note"],
204
+ properties: {
205
+ requirement: { type: "string", description: "미반영 요구·자료" },
206
+ origin: { type: "string", description: "출처 (요구 id 또는 §/자료 좌표)" },
207
+ note: { type: "string", description: "왜 미반영으로 판단했는지" },
208
+ },
209
+ },
210
+ },
211
+ danglingRefs: {
212
+ type: "array",
213
+ description: "[화면.X]·[모델.X]·[자동 처리.X]·[기반.X]·[공통 정의.X] 등이 실존 섹션을 가리키지 않는 참조",
214
+ items: {
215
+ type: "object",
216
+ additionalProperties: false,
217
+ required: ["ref", "usedIn"],
218
+ properties: {
219
+ ref: { type: "string", description: "dangling 참조 ([카테고리.이름])" },
220
+ usedIn: { type: "string", description: "이 참조가 등장한 위치" },
221
+ },
222
+ },
223
+ },
224
+ coverageGaps: {
225
+ type: "array",
226
+ items: { type: "string" },
227
+ description: "쓴 자료 중 spec 에 미반영된 부분 (자료 커버리지 보고)",
228
+ },
229
+ verdict: {
230
+ type: "string",
231
+ enum: ["clean", "issues"],
232
+ description: "검증 종합 — 문제 없으면 clean, 강등·미반영·dangling 중 하나라도 있으면 issues",
233
+ },
234
+ notes: { type: "string", description: "검증 근거·판정 요약" },
235
+ },
236
+ };
237
+
238
+ // ── [Read] spec.md(확정 §1~3) + format-design.md(§4~6 작성법) + SKILL.md(공유 형식) 로딩 ──
239
+ // Derive 단계가 직접 Read 하므로 별도 phase 없이 Derive 안에서 수행.
240
+
241
+ // ── [Derive] §4/§5/§6 헤더 분할 (단일 에이전트) ────────────────
242
+ phase("Derive");
243
+ const derived = await agent(
244
+ `${PRINCIPLES}
245
+
246
+ 너의 일 (설계 배치 1단계 — 설계 분할 도출, 단일 에이전트).
247
+
248
+ 1. spec.md(${SPEC_PATH})를 Read. ${DESIGN_FMT} 의 "설계 분할 절차 (§4/§5/§6 공통)" 절도 Read 해 절차를 따른다.
249
+ 2. 확정된 §1~3(+ 도출된 §7 공통 정의 / §8 도메인 모델 / §9 외부 인터페이스)을 입력으로 삼는다. 헤더가 [OPEN] 상태인 §2/§3 은 본문 미확정이므로 도출 입력에서 제외하되, notes 에 "[OPEN] 섹션 X 미반영" 으로 기록.
250
+ 3. 설계 분할 절차대로 도출:
251
+ - §4 화면: §2 BPMN 의 최종 사용자 액션 노드 + §3 의 사용자 직접 요구 → 트랜잭션/조회 화면. §8 마스터 엔티티 → 마스터 화면. 도메인 묶음·장치별로 분류(category). 유형(kind)·장치(device) 판정.
252
+ - 모달 화면도 별도 화면. 단, 한 화면의 일부 영역(시트·탭)이 다른 화면에서 모달로 재활용되는 경우는 별도 화면으로 분리하지 않음(부모 화면 동작 절에 제약 명시). 전용 모달이 한 곳에서만 쓰이면 부모 §4.x 의 sub-section 으로 포함할 수도 있음 — 이 판단은 reduce 에서 정합하니 일단 후보로 적되 source 에 재활용/전용 여부를 메모.
253
+ - §5 자동 처리: §2/§3 의 시스템 백그라운드 처리(스케줄·이벤트 트리거·표준 채널 외부 자료 수집·적재)를 추출. **명시적 트리거가 있는 것만**.
254
+ - §6 공통·기반 기능: §4/§5 에 속하지 않는 모든 개발 단위 — 부수효과 발동 동작(DataLog·권한 체크·캐시 무효화·알림·감사 로그) + 전역 정적 골격(앱 구조[메뉴·권한·모듈]·로깅·부트스트랩 초기화). §3 의 "시스템 기반" 류 요구가 주 도출원. 프레임워크 기본 기능(매뉴얼 존재)이면 framework=true.
255
+ 4. 요구목록(requirements) 추출: Verify 의 역대조 기준이 된다. §2 BPMN 액션 노드·흐름 설명 bullet 의 룰/조건/계산식·§3 직접 요구·§3 시스템 기반 요구·§7 공통 규격·§9 외부 인터페이스 호출을 빠짐없이 평면 추출(누락 = 검증 실패의 원인). 각 항목에 출처 좌표.
256
+ 5. context: §1~3(+§7~9) 핵심을 map 작성 에이전트가 공유할 수 있게 요약(프로세스 흐름·도메인 모델 필드·외부 자료·외부 인터페이스).
257
+
258
+ 도출 근거가 없는 화면/처리/기반은 만들어내지 말 것(환각 차단). 근거가 모호하면 notes 에 [OPEN] 후보로 기록.
259
+ screens / autoProcesses / commonBases / requirements / context / notes 를 반환.`,
260
+ { label: "derive", phase: "Derive", schema: DERIVE_SCHEMA },
261
+ );
262
+
263
+ if (!derived) throw new Error("[Derive] 설계 분할 도출 에이전트 실행 실패(null) — 중단.");
264
+ const SCREENS = (derived.screens ?? []).filter(Boolean);
265
+ const AUTOS = (derived.autoProcesses ?? []).filter(Boolean);
266
+ const BASES = (derived.commonBases ?? []).filter(Boolean);
267
+ const REQUIREMENTS = (derived.requirements ?? []).filter(Boolean);
268
+ const CONTEXT = derived.context ?? "";
269
+
270
+ if (SCREENS.length + AUTOS.length + BASES.length === 0) {
271
+ throw new Error(
272
+ "§4/§5/§6 도출 결과가 비었습니다. 확정된 §1~3 본문이 spec.md 에 있는지 확인하세요(모두 [OPEN] 이면 설계 입력이 없음).",
273
+ );
274
+ }
275
+ log(
276
+ `도출: 화면 ${SCREENS.length} · 자동 처리 ${AUTOS.length} · 공통·기반 ${BASES.length} / 요구 ${REQUIREMENTS.length}건`,
277
+ );
278
+
279
+ // 도출 컨텍스트 — map 작성 에이전트에 공유.
280
+ const DERIVE_CONTEXT = `
281
+ 설계 분할 도출 결과(§1~3 기반):
282
+
283
+ [화면 목록]
284
+ ${SCREENS.map((s) => `- ${s.name} | ${s.category} | ${s.kind} | ${s.device} (도출: ${s.source})`).join("\n") || "- (없음)"}
285
+
286
+ [자동 처리]
287
+ ${AUTOS.map((a) => `- ${a.name} | 트리거: ${a.trigger} (도출: ${a.source})`).join("\n") || "- (없음)"}
288
+
289
+ [공통·기반]
290
+ ${BASES.map((b) => `- ${b.name} | ${b.framework ? "프레임워크 기본(매뉴얼 참조)" : "bespoke(자유 서술)"} (도출: ${b.source})`).join("\n") || "- (없음)"}
291
+
292
+ [§1~3(+§7~9) 핵심 컨텍스트]
293
+ ${CONTEXT}
294
+ `;
295
+
296
+ // ── [Map] §4/§5/§6 본문 병렬 작성 ──────────────────────────────
297
+ // 각 단위 1 에이전트. 규모에 따라 fan-out 폭이 자동으로 조절됨.
298
+ phase("Map");
299
+
300
+ const screenThunks = SCREENS.map((s) => () =>
301
+ agent(
302
+ `${PRINCIPLES}
303
+
304
+ ${DERIVE_CONTEXT}
305
+
306
+ 너의 일 (§4 화면 본문 작성 — 단위 1건).
307
+
308
+ 대상 화면: ${s.name} (분류: ${s.category} / 유형: ${s.kind} / 장치: ${s.device})
309
+ 도출 근거: ${s.source}
310
+
311
+ 작성 절차:
312
+ 1. ${DESIGN_FMT} 의 "§4 화면" 절 전체(설계 분할 절차 제외 — 화면 정의 표준 구조·와이어프레임·항목·동작·시각 규칙·도메인 규칙·마스터 화면 표준·상단 command 바·모달 처리)를 Read.
313
+ 2. 모범: §4 작성법 첫머리 "모범 (와이어 패턴별)" 에서 이 화면 유형에 맞는 패턴의 § 를 example-spec.md 에서 Read 해 헤더 구조·표 형식을 직접 대조([구현] 마커는 sd-impl 소관이라 모방 금지).
314
+ 3. spec.md(${SPEC_PATH})의 관련 §2/§3 본문 + §7/§8/§9 를 다시 Read 해 이 화면이 충족할 요구·도메인 모델·양식을 근거로 삼는다.
315
+ 4. "§4.x 표준 구조" 순서대로 본문을 작성: 헤더 인덱스(Actor·관련 섹션) → 기능 개요 → 와이어프레임 → 항목(영역별 표) → 동작 → 시각 규칙(해당 시) → 도메인 규칙·로직(해당 시) → 자유 추가 sub-section(양식 입출력 등 해당 시).
316
+ - 와이어프레임: 영역 배치·구획만. 디테일은 항목/동작/도메인 규칙 절이 단일 출처. ASCII 박스·영역 추상화 표기를 임의 변형 금지.
317
+ - 항목 표: 도메인 매핑은 \`[모델.X.Y]\` 형식. 매핑 없으면 \`-\`. 필수 없으면 \`-\`. 비고에 비자명 표시 내용·편집 방법.
318
+ - 마스터 유형이면 "마스터 화면 표준"(선택 체크박스·ID 편집 버튼·시트 상단 버튼바·ID 내림차순) 적용.
319
+ - 모달: 호출하는 쪽 동작에 "→ [화면.X] 을 모달로 띄움" 명시. 일부 영역 재활용 시 동작 절 끝에 영역별 제약(편집 가능/선택 전용/multiselect) 한 줄씩.
320
+ 5. 헤더 텍스트는 \`### {화면명} (${s.device})\` 형식 — §번호와 날짜 마커는 reduce 에서 부여하므로 markdown 에 §번호를 넣지 말 것(헤더 텍스트만). [확정] 마커 금지.
321
+ 6. 근거 있는 비자명 항목엔 줄끝 (근거: 출처). 근거 없는 값(예: 그리드 행/열 수, 미정 규격)은 임의로 채우지 말고 [OPEN] 으로.
322
+
323
+ markdown(완성 본문) + relatedRefs(참조한 [카테고리.이름] 전부) + opens([OPEN] 항목) + evidence(부착한 근거 '<항목>: <출처>') 를 반환. section="화면", title="${s.name}".`,
324
+ { label: `map:screen:${s.name}`, phase: "Map", schema: SECTION_SCHEMA },
325
+ ),
326
+ );
327
+
328
+ const autoThunks = AUTOS.map((a) => () =>
329
+ agent(
330
+ `${PRINCIPLES}
331
+
332
+ ${DERIVE_CONTEXT}
333
+
334
+ 너의 일 (§5 자동 처리 본문 작성 — 단위 1건).
335
+
336
+ 대상 자동 처리: ${a.name} (트리거: ${a.trigger})
337
+ 도출 근거: ${a.source}
338
+
339
+ 작성 절차:
340
+ 1. ${DESIGN_FMT} 의 "§5 자동 처리" 절 전체(정의·본문 구조·처리·예외 처리·양식 입출력)를 Read.
341
+ 2. 모범: example-spec.md §5.1 을 Read 해 형식을 직접 대조.
342
+ 3. spec.md(${SPEC_PATH})의 관련 §2/§3 + §7/§8/§9 를 다시 Read.
343
+ 4. 본문을 작성: 평문(목적: / 트리거: / 관련 섹션:) → #### 처리 → #### 예외 처리 → 자유 추가 sub-section(읽기/쓰기 양식 등 해당 시).
344
+ - 예외 처리는 실패 케이스별 위험·대처·재시도 한계.
345
+ - §5 는 명시적 스케줄·이벤트 트리거가 핵심. 표준 프로토콜·채널 외부 자료 수집·적재도 §5(시스템별 협상 없는 표준 인터페이싱).
346
+ 5. 헤더 텍스트는 \`### {자동 처리명}\` (§번호·날짜 마커는 reduce 에서). markdown 에 §번호 금지. [확정] 마커 금지.
347
+ 6. 비자명 항목엔 줄끝 (근거: 출처). 근거 없으면 [OPEN].
348
+
349
+ markdown + relatedRefs + opens + evidence 를 반환. section="자동 처리", title="${a.name}".`,
350
+ { label: `map:auto:${a.name}`, phase: "Map", schema: SECTION_SCHEMA },
351
+ ),
352
+ );
353
+
354
+ const baseThunks = BASES.map((b) => () =>
355
+ agent(
356
+ `${PRINCIPLES}
357
+
358
+ ${DERIVE_CONTEXT}
359
+
360
+ 너의 일 (§6 공통·기반 기능 본문 작성 — 단위 1건).
361
+
362
+ 대상 공통·기반 기능: ${b.name} (${b.framework ? "프레임워크 기본 기능 후보(매뉴얼 존재)" : "앱 고유 bespoke 후보"})
363
+ 도출 근거: ${b.source}
364
+
365
+ 작성 절차:
366
+ 1. ${DESIGN_FMT} 의 "§6 공통·기반 기능" 절 전체(정의·본문 구조·프레임워크 기본/ bespoke 두 갈래)를 Read.
367
+ 2. 모범: example-spec.md §6.1(프레임워크 기본=참조 매뉴얼 stub)·§6.2(bespoke=자유 서술)를 Read 해 형식을 직접 대조.
368
+ 3. spec.md(${SPEC_PATH})의 관련 §3(시스템 기반 류 요구)·관련 §2/§7/§8 를 다시 Read.
369
+ 4. 두 갈래로 작성:
370
+ - 프레임워크 기본 기능(앱 구조·로깅·부트스트랩·데이터 변경 이력 등 매뉴얼 존재)이라면 → 평문 라벨(목적: / 트리거·적용 범위:(해당 시) / 관련 섹션:) + \`참조 매뉴얼:\` 한 줄. 처리·구성·예외 detail 은 적지 않음(매뉴얼 위임).
371
+ - 참조 매뉴얼 파일명이 실제 존재하는지 \`.claude/references/sd-simplysm14/manuals/\` 를 Glob 으로 확인. 확신 없으면 파일명을 추정으로 적되 \`[OPEN] 참조 매뉴얼명 미확인\` 메모.
372
+ - bespoke(앱 고유 부수효과, 매뉴얼 없음)라면 → 평문 라벨 + 본문 한두 줄 자유 서술(설계 고도까지만, 구현 디테일 금지). 정말 필요한 정보만 h4 자유 추가.
373
+ 5. 헤더 텍스트는 \`### {기능명}\` (§번호·날짜 마커는 reduce 에서). markdown 에 §번호 금지. [확정] 마커 금지.
374
+ 6. 비자명 항목엔 줄끝 (근거: 출처). 근거 없으면 [OPEN].
375
+
376
+ markdown + relatedRefs + opens + evidence 를 반환. section="기반", title="${b.name}".`,
377
+ { label: `map:base:${b.name}`, phase: "Map", schema: SECTION_SCHEMA },
378
+ ),
379
+ );
380
+
381
+ const mappedRaw = await parallel([...screenThunks, ...autoThunks, ...baseThunks]);
382
+ assertNoFailures(mappedRaw, "Map", [
383
+ ...SCREENS.map((s) => `map:screen:${s.name}`),
384
+ ...AUTOS.map((a) => `map:auto:${a.name}`),
385
+ ...BASES.map((b) => `map:base:${b.name}`),
386
+ ]);
387
+ const mapped = mappedRaw;
388
+
389
+ const screenSections = mapped.filter((m) => m.section === "화면");
390
+ const autoSections = mapped.filter((m) => m.section === "자동 처리");
391
+ const baseSections = mapped.filter((m) => m.section === "기반");
392
+ if (screenSections.length !== SCREENS.length || autoSections.length !== AUTOS.length || baseSections.length !== BASES.length)
393
+ throw new Error(
394
+ `[Map] 작성 단위 수 불일치 — 화면 ${screenSections.length}/${SCREENS.length}·자동 ${autoSections.length}/${AUTOS.length}·기반 ${baseSections.length}/${BASES.length}. 중단.`,
395
+ );
396
+ log(
397
+ `본문 작성: 화면 ${screenSections.length}/${SCREENS.length} · 자동 처리 ${autoSections.length}/${AUTOS.length} · 공통·기반 ${baseSections.length}/${BASES.length}`,
398
+ );
399
+
400
+ // ── [Reduce] 교차참조 정합 (단일 에이전트) ─────────────────────
401
+ // 화면 목록 표 + §번호 부여 + 모달/시트 재활용 교차참조 정합.
402
+ phase("Reduce");
403
+
404
+ const SECTIONS_DUMP = (label, arr) =>
405
+ `=== ${label} ===\n` +
406
+ arr
407
+ .map(
408
+ (m) =>
409
+ `--- [${m.section}.${m.title}] ---\nrelatedRefs: ${(m.relatedRefs ?? []).join(", ") || "(없음)"}\nopens: ${(m.opens ?? []).join(" | ") || "(없음)"}\n\n${m.markdown}\n`,
410
+ )
411
+ .join("\n");
412
+
413
+ const REDUCE_SCHEMA = {
414
+ type: "object",
415
+ additionalProperties: false,
416
+ required: ["screenTable", "sections", "consistencyNotes"],
417
+ properties: {
418
+ screenTable: {
419
+ type: "string",
420
+ description: "§4 첫머리 화면 목록 표 (markdown). 5컬럼 `§ | 분류 | 화면 | 유형 | 장치`, 같은 분류 인접, §번호는 4.1부터",
421
+ },
422
+ sections: {
423
+ type: "array",
424
+ description: "§번호가 부여되고 교차참조가 정합된 최종 섹션들 (순서대로 §4 → §5 → §6)",
425
+ items: {
426
+ type: "object",
427
+ additionalProperties: false,
428
+ required: ["group", "number", "title", "markdown"],
429
+ properties: {
430
+ group: { type: "string", enum: ["화면", "자동 처리", "기반"] },
431
+ number: { type: "string", description: "부여된 §번호 (예: 4.1, 5.1, 6.1)" },
432
+ title: { type: "string", description: "단위명 ([카테고리.이름] 의 이름)" },
433
+ markdown: { type: "string", description: "§번호가 부여된 헤더(### N.N …)부터의 최종 본문" },
434
+ },
435
+ },
436
+ },
437
+ consistencyNotes: {
438
+ type: "string",
439
+ description: "정합에서 변경·통합한 내용 (모달 병합·재활용 제약 부착·번호 부여·관련 섹션 보정 등)",
440
+ },
441
+ },
442
+ };
443
+
444
+ const reduced = await agent(
445
+ `${PRINCIPLES}
446
+
447
+ ${DERIVE_CONTEXT}
448
+
449
+ 너의 일 (설계 배치 reduce — 교차참조 정합, 단일 에이전트). map 단계가 병렬로 작성한 §4/§5/§6 본문을 받아 일관성을 결정한다.
450
+
451
+ ${SECTIONS_DUMP("§4 화면 (작성 본문)", screenSections)}
452
+
453
+ ${SECTIONS_DUMP("§5 자동 처리 (작성 본문)", autoSections)}
454
+
455
+ ${SECTIONS_DUMP("§6 공통·기반 (작성 본문)", baseSections)}
456
+
457
+ 정합 작업:
458
+ 1. §번호 부여: §4 화면은 같은 분류(category)끼리 인접하도록 정렬해 4.1, 4.2 … / §5 는 5.1 … / §6 는 6.1 …. 부여한 번호를 각 본문 헤더에 반영(### N.N {제목} (장치) 형식, 화면은 장치 표기 유지, 자동/기반은 장치 표기 없음). [확정] 마커 금지.
459
+ 2. 화면 목록 표 작성: §4 첫머리 5컬럼 표 \`§ | 분류 | 화면 | 유형 | 장치\`. 같은 분류 인접 배치.
460
+ 3. 모달/시트 재활용 교차참조 정합:
461
+ - 호출하는 쪽 동작의 "→ [화면.X] 을 모달로 띄움" 과 호출되는 §4.x 가 양쪽 다 존재하는지 확인. 한쪽만 있으면 보정(누락측 추가 또는 관련 섹션 보강).
462
+ - 한 화면의 일부 영역이 다른 화면에서 모달로 재활용되는 경우: 별도 화면으로 분리하지 말고 부모 화면 동작 절 끝에 영역별 제약(편집 가능/선택 전용/multiselect)을 한 줄씩. 호출 쪽 동작은 "→ [화면.X] 의 <영역> 을 모달로 띄움".
463
+ - 전용 모달이 한 호출처에서만 쓰이면 부모 §4.x 의 sub-section(h4/h5)으로 포함할지, 별도 §4.x 로 둘지 일관되게 결정.
464
+ - 화면 목록 표는 별도 §4.x 로 둔 화면만 행으로 가짐(부모에 흡수된 전용 모달은 행 없음 — example 의 표 아래 한 줄 설명처럼 처리).
465
+ 4. 관련 섹션 한 줄 정합: 각 본문의 \`관련 섹션:\` 이 실제 참조와 일치하는지, 양방향 의존(A→B 면 B 의 관련 섹션에 A 고려)을 보정.
466
+ 5. 도메인 모델·외부 인터페이스 참조([모델.X]·[외부 인터페이스.X])가 §8/§9 에 실존하는지 확인하고, 본문이 새 엔티티·새 외부 호출을 전제로 하면 consistencyNotes 에 "§8/§9 신규 필요" 로 기록(이 배치는 §4~6 만 write 하므로 §8/§9 는 건드리지 않음 — 검토 패키지 보고용).
467
+
468
+ 내용물(항목·동작·필드)은 임의로 바꾸지 말 것 — 번호·교차참조·재활용 제약·관련 섹션 정합만 수행. 무관 본문은 보존.
469
+ screenTable / sections(§번호 부여된 최종 본문, §4→§5→§6 순서) / consistencyNotes 를 반환.`,
470
+ { label: "reduce", phase: "Reduce", schema: REDUCE_SCHEMA },
471
+ );
472
+
473
+ if (!reduced) throw new Error("[Reduce] 교차참조 정합 에이전트 실행 실패(null) — 중단.");
474
+ const FINAL_SECTIONS = (reduced.sections ?? []).filter(Boolean);
475
+ if (FINAL_SECTIONS.length === 0) throw new Error("reduce 가 정합된 섹션을 반환하지 않았습니다.");
476
+ log(`정합 완료: 섹션 ${FINAL_SECTIONS.length}개, 화면 목록 표 생성`);
477
+
478
+ // ── [Verify] 독립 컨텍스트 fan-out 검증 ────────────────────────
479
+ // 1) 근거 1:1 대조 2) 원자료↔spec 전수 대조(누락) 3) §1~3 요구목록→§4~6 역대조 4) dangling 참조 grep · 커버리지
480
+ phase("Verify");
481
+
482
+ // 전체 §4~6 본문 묶음 (검증 입력).
483
+ // s.markdown 은 이미 §번호 헤더(### N.N …)를 포함하므로 그대로 사용(헤더 중복 방지).
484
+ const FINAL_DUMP = FINAL_SECTIONS.map((s) => s.markdown).join("\n\n");
485
+ const ALL_EVIDENCE = mapped.flatMap((m) =>
486
+ (m.evidence ?? []).map((e) => `[${m.section}.${m.title}] ${e}`),
487
+ );
488
+ const ALL_REFS = [...new Set(mapped.flatMap((m) => m.relatedRefs ?? []))];
489
+
490
+ // 1) 근거 1:1 대조 (병렬, 근거별)
491
+ const evidenceThunks = ALL_EVIDENCE.map((ev) => () =>
492
+ agent(
493
+ `${PRINCIPLES}
494
+
495
+ 너의 일 (verify — 근거 위조 검증, 독립 컨텍스트 1건).
496
+
497
+ 설계 본문에 부착된 다음 (근거: …) 항목이 원자료에서 실제로 그 내용을 담는지 원자료를 직접 Read/Grep 해 1:1 대조하라.
498
+
499
+ 근거 항목: ${ev}
500
+
501
+ 판정:
502
+ - spec 항목이 주장한 출처 좌표를 실제 자료에서 열어 내용이 일치하면 forgedEvidence 비움.
503
+ - 좌표가 그 내용을 담지 않으면(과장·오인·없음) forgedEvidence 에 {item, claimedSource, finding} 으로 보고 → [OPEN] 강등 대상.
504
+ 나머지 필드(uncovered/danglingRefs/coverageGaps)는 이 잡에서 비워도 됨. verdict 는 forgedEvidence 유무로.`,
505
+ { label: `verify:evidence`, phase: "Verify", schema: VERIFY_SCHEMA },
506
+ ),
507
+ );
508
+
509
+ // 2)+3) 요구목록 역대조 + 원자료↔spec 누락 대조 (단일 통합 잡 — 전체 시야 필요)
510
+ const reverseThunk = () =>
511
+ agent(
512
+ `${PRINCIPLES}
513
+
514
+ 너의 일 (verify — §1~3 요구목록 → §4~6 역대조 + 원자료↔spec 누락 대조, 전체 시야 1건).
515
+
516
+ 아래는 설계 분할 단계에서 §1~3(+§7~9)에서 추출한 요구목록이다. 각 요구가 §4~6 본문에서 실제로 커버되는지 역대조하라. 동시에 spec.md(${SPEC_PATH})의 §1~3·§7~9 원본을 직접 Read 해, 요구목록에 빠졌으나 §4~6 이 마땅히 커버해야 할 부분도 함께 대조하라(요구목록 자체의 누락도 검증).
517
+
518
+ 요구목록:
519
+ ${REQUIREMENTS.map((r) => `- ${r.id}: ${r.text} (출처: ${r.origin})`).join("\n") || "- (없음)"}
520
+
521
+ 작성된 §4~6 본문:
522
+ ${FINAL_DUMP}
523
+
524
+ 판정:
525
+ - §4~6 중 어느 곳에서도 커버되지 않는 요구는 uncovered 에 {requirement, origin, note} 로 보고.
526
+ - 단, [OPEN] 으로 정당하게 보류된 항목은 누락이 아님 — note 에 "[OPEN] 처리됨" 으로 구분.
527
+ forgedEvidence/danglingRefs/coverageGaps 는 비워도 됨. verdict 는 uncovered 유무로.`,
528
+ { label: `verify:reverse`, phase: "Verify", schema: VERIFY_SCHEMA },
529
+ );
530
+
531
+ // 4) dangling 참조 + 커버리지 (단일 잡 — spec 전체 grep)
532
+ const danglingThunk = () =>
533
+ agent(
534
+ `${PRINCIPLES}
535
+
536
+ 너의 일 (verify — dangling 참조 검증 + 자료 커버리지, 전체 시야 1건).
537
+
538
+ [dangling] 아래는 §4~6 본문이 사용한 [카테고리.이름] 참조 전부다. 각 참조가 실존 섹션을 가리키는지 spec.md(${SPEC_PATH}) 전체를 Read/Grep 해 확인하라.
539
+ - [화면.X] → §4 에 화면 X 실존?
540
+ - [자동 처리.X] → §5 에 실존?
541
+ - [기반.X] → §6 에 실존?
542
+ - [모델.X] / [공통 정의.X] / [외부 인터페이스.X] → §8/§7/§9 에 실존? (이 배치는 §4~6 만 write 하므로, §7~9 가 아직 없으면 "§7~9 신규 필요" 로 보고)
543
+ 실존하지 않으면 danglingRefs 에 {ref, usedIn} 으로 보고.
544
+
545
+ 참조 목록:
546
+ ${ALL_REFS.map((r) => `- ${r}`).join("\n") || "- (없음)"}
547
+
548
+ 작성된 §4~6 본문:
549
+ ${FINAL_DUMP}
550
+
551
+ [커버리지] 설계에 쓴 자료(§1~3·§7~9 및 그것이 인용한 원자료) 중 §4~6 에 미반영된 부분을 coverageGaps 에 보고.
552
+
553
+ forgedEvidence/uncovered 는 비워도 됨. verdict 는 danglingRefs/coverageGaps 유무로.`,
554
+ { label: `verify:dangling`, phase: "Verify", schema: VERIFY_SCHEMA },
555
+ );
556
+
557
+ const verifyResults = await parallel([...evidenceThunks, reverseThunk, danglingThunk]);
558
+ assertNoFailures(verifyResults, "Verify", [
559
+ ...ALL_EVIDENCE.map((ev) => `verify:evidence:${ev.slice(0, 40)}`),
560
+ "verify:reverse",
561
+ "verify:dangling",
562
+ ]);
563
+
564
+ const forgedEvidence = verifyResults.flatMap((v) => v.forgedEvidence ?? []);
565
+ const uncovered = verifyResults.flatMap((v) => v.uncovered ?? []);
566
+ const danglingRefs = verifyResults.flatMap((v) => v.danglingRefs ?? []);
567
+ const coverageGaps = [...new Set(verifyResults.flatMap((v) => v.coverageGaps ?? []))];
568
+ log(
569
+ `검증: 근거위조 ${forgedEvidence.length} · 미반영 ${uncovered.length} · dangling ${danglingRefs.length} · 커버리지갭 ${coverageGaps.length}`,
570
+ );
571
+
572
+ // ── [Write] §4~6 을 spec.md 에 반영 ────────────────────────────
573
+ // 근거 위조분은 [OPEN] 강등하고, 정합된 §4~6 본문 + 화면 목록 표를 spec.md 의 ## 4./## 5./## 6. 자리에 써넣는다.
574
+ phase("Write");
575
+
576
+ const screenFinal = FINAL_SECTIONS.filter((s) => s.group === "화면");
577
+ const autoFinal = FINAL_SECTIONS.filter((s) => s.group === "자동 처리");
578
+ const baseFinal = FINAL_SECTIONS.filter((s) => s.group === "기반");
579
+
580
+ const WRITE_SCHEMA = {
581
+ type: "object",
582
+ additionalProperties: false,
583
+ required: ["written", "demoted", "summary"],
584
+ properties: {
585
+ written: {
586
+ type: "array",
587
+ items: { type: "string" },
588
+ description: "spec.md 에 써넣은 섹션 목록 (§번호 + 제목)",
589
+ },
590
+ demoted: {
591
+ type: "array",
592
+ items: { type: "string" },
593
+ description: "근거 위조로 [OPEN] 강등한 항목 목록",
594
+ },
595
+ summary: { type: "string", description: "write 결과 요약 (어느 헤더 아래에 어떻게 반영했는지)" },
596
+ },
597
+ };
598
+
599
+ const written = await agent(
600
+ `${PRINCIPLES}
601
+
602
+ 너의 일 (설계 배치 write — §4~6 을 spec.md 에 반영). spec.md(${SPEC_PATH})를 Read 한 뒤 Edit 으로 §4/§5/§6 본문을 써넣는다.
603
+
604
+ [근거 위조 강등] 아래 항목들은 verify 에서 근거가 원자료와 불일치로 확인됨 → 해당 항목의 (근거: …)를 제거하고 [OPEN] 으로 강등한 채 써넣어라(필요 시 "[OPEN] <항목> — 근거 위조 의심(주장출처: <claimedSource>), 재확인 필요" 메모로 원 좌표 보존).
605
+ ${forgedEvidence.length ? forgedEvidence.map((f) => `- ${f.item} (주장출처: ${f.claimedSource} / 불일치: ${f.finding})`).join("\n") : "- (없음)"}
606
+
607
+ [써넣을 §4 화면] — 화면 목록 표를 ## 4. 화면 헤더 바로 아래에 먼저, 이어서 각 화면 본문(### 4.x …)을 순서대로:
608
+
609
+ 화면 목록 표:
610
+ ${reduced.screenTable}
611
+
612
+ 화면 본문:
613
+ ${screenFinal.map((s) => s.markdown).join("\n\n") || "(없음)"}
614
+
615
+ [써넣을 §5 자동 처리] — ## 5. 자동 처리 헤더 아래에 순서대로:
616
+ ${autoFinal.map((s) => s.markdown).join("\n\n") || "(없음)"}
617
+
618
+ [써넣을 §6 공통·기반 기능] — ## 6. 공통·기반 기능 헤더 아래에 순서대로:
619
+ ${baseFinal.map((s) => s.markdown).join("\n\n") || "(없음)"}
620
+
621
+ 반영 방법:
622
+ - spec.md 의 기존 \`## 4. 화면\` / \`## 5. 자동 처리\` / \`## 6. 공통·기반 기능\` 헤더는 유지하고, 그 헤더와 다음 \`## \` 헤더 사이의 내용만 위 내용으로 채운다(헤더 자체·§1~3·§7~10 은 절대 건드리지 말 것 — 무관 섹션 보존, 일괄 치환 금지).
623
+ - 헤더가 이미 본문을 갖고 있으면(재설계) 해당 §4~6 영역만 교체.
624
+ - 헤더 §번호·텍스트는 reduce 가 부여한 그대로. [확정] 마커 금지. 화면 헤더는 (PC)/(PDA) 장치 표기 유지.
625
+ - Edit 으로 정확히 그 영역만 치환. 여러 번 Edit 해도 됨.
626
+
627
+ written(써넣은 섹션) / demoted(강등 항목) / summary 를 반환.`,
628
+ { label: "write", phase: "Write", schema: WRITE_SCHEMA },
629
+ );
630
+
631
+ if (!written) throw new Error("[Write] §4~6 반영 에이전트 실행 실패(null) — spec.md 반영 미완료, 중단.");
632
+ log(`반영 완료: ${(written.written ?? []).length}개 섹션 · 강등 ${(written.demoted ?? []).length}건`);
633
+
634
+ // ── 결과 반환: 검토 패키지(쓴 섹션 요약 + [OPEN] + verify + 커버리지) ──
635
+ const ALL_OPENS = mapped.flatMap((m) => (m.opens ?? []).map((o) => `[${m.section}.${m.title}] ${o}`));
636
+
637
+ return {
638
+ specPath: SPEC_PATH,
639
+ written: written?.written ?? [],
640
+ demoted: written?.demoted ?? [],
641
+ screenTable: reduced.screenTable,
642
+ derived: {
643
+ screens: SCREENS.map((s) => ({ name: s.name, category: s.category, kind: s.kind, device: s.device })),
644
+ autoProcesses: AUTOS.map((a) => ({ name: a.name, trigger: a.trigger })),
645
+ commonBases: BASES.map((b) => ({ name: b.name, framework: b.framework })),
646
+ notes: derived?.notes ?? "",
647
+ },
648
+ consistencyNotes: reduced.consistencyNotes,
649
+ opens: ALL_OPENS,
650
+ verify: {
651
+ forgedEvidence,
652
+ uncovered,
653
+ danglingRefs,
654
+ coverageGaps,
655
+ },
656
+ summary: {
657
+ screens: screenFinal.length,
658
+ autoProcesses: autoFinal.length,
659
+ commonBases: baseFinal.length,
660
+ requirements: REQUIREMENTS.length,
661
+ opens: ALL_OPENS.length,
662
+ forged: forgedEvidence.length,
663
+ uncovered: uncovered.length,
664
+ dangling: danglingRefs.length,
665
+ coverageGaps: coverageGaps.length,
666
+ },
667
+ };