@simplysm/sd-claude 14.0.94 → 14.0.95

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 (31) hide show
  1. package/claude/references/sd-simplysm14/README.md +6 -2
  2. package/claude/references/sd-simplysm14/apis/angular/README.md +180 -43
  3. package/claude/references/sd-simplysm14/apis/angular/controls.md +275 -125
  4. package/claude/references/sd-simplysm14/apis/angular/crud.md +54 -59
  5. package/claude/references/sd-simplysm14/apis/angular/directives.md +139 -48
  6. package/claude/references/sd-simplysm14/apis/angular/features.md +102 -88
  7. package/claude/references/sd-simplysm14/apis/angular/kanban.md +54 -0
  8. package/claude/references/sd-simplysm14/apis/angular/layout.md +60 -36
  9. package/claude/references/sd-simplysm14/apis/angular/overlay.md +127 -75
  10. package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +97 -51
  11. package/claude/references/sd-simplysm14/apis/angular/shared-data.md +74 -58
  12. package/claude/references/sd-simplysm14/apis/angular/sheet.md +81 -60
  13. package/claude/references/sd-simplysm14/apis/excel/README.md +5 -5
  14. package/claude/references/sd-simplysm14/apis/excel/cell.md +3 -3
  15. package/claude/references/sd-simplysm14/apis/excel/style.md +2 -2
  16. package/claude/references/sd-simplysm14/apis/excel/workbook-worksheet.md +5 -4
  17. package/claude/references/sd-simplysm14/apis/excel/wrapper.md +2 -2
  18. package/claude/references/sd-simplysm14/manuals/client-app-structure.md +4 -2
  19. package/claude/references/sd-simplysm14/manuals/client-component.md +23 -23
  20. package/claude/references/sd-simplysm14/manuals/client-crud.md +151 -4
  21. package/claude/references/sd-simplysm14/manuals/client-demo.md +5 -18
  22. package/claude/references/sd-simplysm14/manuals/client-shared-data.md +5 -2
  23. package/claude/references/sd-simplysm14/manuals/data-log.md +1 -1
  24. package/claude/sd-system-prompt.md +7 -0
  25. package/claude/skills/sd-debug/SKILL.md +142 -27
  26. package/claude/skills/sd-review/SKILL.md +158 -20
  27. package/claude/skills/sd-spec/SKILL.md +1 -0
  28. package/package.json +1 -1
  29. package/claude/references/sd-simplysm14/apis/angular/infra.md +0 -82
  30. package/claude/skills/sd-debug/workflow.js +0 -390
  31. package/claude/skills/sd-review/workflow.js +0 -324
@@ -1,390 +0,0 @@
1
- export const meta = {
2
- name: "sd-debug",
3
- description:
4
- "버그·실패·예외의 원인 가설을 다관점으로 발굴하고 가설별로 검증→해결책→적대검증을 거쳐 검증된 해결책을 구조화 보고하는 멀티에이전트 디버깅",
5
- phases: [
6
- { title: "Hypothesize", detail: "관점 동적 생성 + 관점별 병렬 추출 + 자유탐색 + 의미 중복제거" },
7
- { title: "Verify", detail: "가설별 검증→해결책 통합 (명백 오류·근거 전무 기각, 통과 시 근본 우선 해결책 ≤2, 무배리어)" },
8
- { title: "Adversarial", detail: "가설+해결책 쌍별 4관점 적대검증 veto+다수결 (무배리어)" },
9
- ],
10
- };
11
-
12
- // ── 입력 ───────────────────────────────────────────────────────
13
- // args: 문제 설명(필수). 에러·스택·재현조건·코드경로·환경은 선택.
14
- // 대화 맥락에서 호출되면 메인 루프가 위 정보를 요약해 args 로 전달.
15
- if (args == null || (typeof args === "string" && args.trim() === "")) {
16
- throw new Error(
17
- "디버깅할 문제 설명을 args 로 전달하세요 (증상·기대동작·관찰결과 등; 에러/재현조건/코드경로는 선택).",
18
- );
19
- }
20
- const problem = typeof args === "string" ? args : JSON.stringify(args);
21
-
22
- // ── 공통 원칙(모든 단계 주입) ──────────────────────────────────
23
- const PRINCIPLES = `
24
- 디버깅 원칙:
25
- - 모든 판정은 실제 코드/설정을 Read 하여 확인. 근거 없는 추측·일반론 금지(추측은 '가설'로만 등록, 사실로 단정 금지).
26
- - 현재 워킹트리만 기준. git log/diff/show/blame 등 과거 조회 금지. .back/ 및 .gitignore 등재 경로(node_modules·dist·.tmp 등) 읽지 말 것.
27
- - 결측(null/undefined)은 결측대로 다룰 것. 빈 값을 추측으로 채우지 말 것.
28
- - 입력 정보가 부족하면 Grep/Glob/Read 로 코드베이스를 직접 조사해 보강.
29
- `;
30
-
31
- // ── fail-fast 가드 ─────────────────────────────────────────────
32
- // parallel/pipeline 배리어 직후 호출. 결과에 null(에이전트 reject/스킵)이 하나라도 있으면
33
- // 부분 결과로 진행하지 않고 즉시 throw. 정상이지만 빈 결과(빈 배열 등)는 null 이 아니라 통과.
34
- function assertNoFailures(results, stage, labels) {
35
- const failed = results.flatMap((r, i) => (r ? [] : [labels?.[i] ?? `#${i}`]));
36
- if (failed.length > 0) {
37
- throw new Error(
38
- `[${stage}] 에이전트 ${failed.length}/${results.length}건 실행 실패(null) — 부분 결과로 진행 금지(fail-fast). 실패: ${failed.join(", ")}. resume 로 재실행하면 성공분은 캐시됩니다.`,
39
- );
40
- }
41
- }
42
-
43
- // ── 적대검증 관점 4개 ──────────────────────────────────────────
44
- const LENSES = [
45
- { key: "causal", title: "인과", focus: "해결책이 이 가설의 원인을 실제로 제거하는가. 원인-증상 인과가 성립하는가." },
46
- {
47
- key: "regression",
48
- title: "회귀·부작용",
49
- focus:
50
- "해결책이 새 버그·룰 위반·엣지케이스(결측 null/undefined·동시성/트랜잭션·soft delete 동명 레코드·권한 분기·타입/스키마 제약)를 유발하는가.",
51
- },
52
- { key: "evidence", title: "증거 정합", focus: "가설의 근거가 실제 코드/스택과 일치하는가. 오인·과장은 없는가." },
53
- { key: "alternative", title: "대안 원인", focus: "이 가설 말고 다른 원인이 진짜일 가능성은 없는가." },
54
- ];
55
-
56
- // ── 의심 범주 예시(단일 소스 — 스키마 설명·관점 도출 프롬프트가 공유) ──
57
- const PERSPECTIVE_EXAMPLES =
58
- "동시성·타이밍, 데이터·결측, 타입·계약, 로직·경계조건, 외부의존·환경·설정, 상태·생명주기";
59
-
60
- // ── 스키마 ─────────────────────────────────────────────────────
61
- const PERSPECTIVES_SCHEMA = {
62
- type: "object",
63
- additionalProperties: false,
64
- required: ["perspectives", "notes"],
65
- properties: {
66
- perspectives: {
67
- type: "array",
68
- items: {
69
- type: "object",
70
- additionalProperties: false,
71
- required: ["key", "title", "focus"],
72
- properties: {
73
- key: { type: "string" },
74
- title: { type: "string", description: `의심 범주 이름(예: ${PERSPECTIVE_EXAMPLES})` },
75
- focus: { type: "string", description: "이 관점이 의심하는 구체 지점" },
76
- },
77
- },
78
- },
79
- notes: { type: "string", description: "증상에서 이 관점들을 고른 근거" },
80
- },
81
- };
82
-
83
- const HYPOTHESES_SCHEMA = {
84
- type: "object",
85
- additionalProperties: false,
86
- required: ["hypotheses"],
87
- properties: {
88
- hypotheses: {
89
- type: "array",
90
- items: {
91
- type: "object",
92
- additionalProperties: false,
93
- required: ["title", "cause", "perspective", "evidenceExpected", "refuteSignal"],
94
- properties: {
95
- title: { type: "string", description: "가설 한 줄 요약" },
96
- cause: { type: "string", description: "원인 가설 상세(무엇이 어떻게 증상을 일으키는가)" },
97
- perspective: { type: "string", description: "이 가설이 나온 관점(자유탐색이면 'free')" },
98
- evidenceExpected: { type: "string", description: "이 가설이 맞다면 코드/로그에서 보일 근거" },
99
- refuteSignal: { type: "string", description: "이 가설이 틀렸다면 보일 반증 신호" },
100
- },
101
- },
102
- },
103
- },
104
- };
105
-
106
- const VERIFY_SOLVE_SCHEMA = {
107
- type: "object",
108
- additionalProperties: false,
109
- required: ["verdict", "reason", "refinedCause", "solutions"],
110
- properties: {
111
- verdict: {
112
- type: "string",
113
- enum: ["confirmed", "uncertain", "rejected"],
114
- description: "confirmed=근거 확인, uncertain=일부라도 코드로 뒷받침(통과), rejected=명백히 틀림 또는 근거 전무+반증 우세",
115
- },
116
- reason: { type: "string", description: "코드를 Read 해 확인한 판정 근거(근거 인용)" },
117
- refinedCause: { type: "string", description: "검증으로 구체화된 원인(통과 시). rejected 면 빈 문자열" },
118
- solutions: {
119
- type: "array",
120
- description: "통과(confirmed/uncertain) 시 근본 원인 직접 제거 정도가 높은 순으로 정렬한 해결책 후보(최대 2). rejected 면 빈 배열",
121
- items: {
122
- type: "object",
123
- additionalProperties: false,
124
- required: ["approach", "mechanism", "changeScope"],
125
- properties: {
126
- approach: { type: "string", description: "해결 접근 한 줄" },
127
- mechanism: { type: "string", description: "이 접근이 원인을 어떻게 제거하는가" },
128
- changeScope: { type: "string", description: "대략의 수정 범위(파일·함수)" },
129
- },
130
- },
131
- },
132
- },
133
- };
134
-
135
- const ADVERSARIAL_SCHEMA = {
136
- type: "object",
137
- additionalProperties: false,
138
- required: ["lenses"],
139
- properties: {
140
- lenses: {
141
- type: "array",
142
- minItems: LENSES.length,
143
- description: "각 적대검증 관점의 판정 (LENSES 4개 전부, 관점마다 1개 항목)",
144
- items: {
145
- type: "object",
146
- additionalProperties: false,
147
- required: ["lens", "pass", "critical", "reason", "revisedNote"],
148
- properties: {
149
- lens: { type: "string", description: "이 판정의 관점 이름" },
150
- pass: { type: "boolean", description: "이 관점에서 해결책이 통과하는가(true=문제없음)" },
151
- critical: {
152
- type: "boolean",
153
- description: "발견한 결함이 치명적인가(회귀 유발·인과 불성립은 true). pass=true 면 false",
154
- },
155
- reason: { type: "string", description: "코드 확인 기반 판정 근거" },
156
- revisedNote: { type: "string", description: "결함 있으면 교정 제안. 없으면 빈 문자열" },
157
- },
158
- },
159
- },
160
- },
161
- };
162
-
163
- // ── 단계별 프롬프트 ────────────────────────────────────────────
164
- const verifySolvePrompt = (h) => `문제: ${problem}
165
-
166
- ${PRINCIPLES}
167
-
168
- 너의 일: 아래 원인 가설을 실제 코드를 Read 해 검증하고(관대 기준), 통과하면 같은 코드 근거 위에서 해결책까지 도출하라.
169
-
170
- 1) 검증:
171
- - confirmed: 코드에서 근거를 확인함.
172
- - uncertain: 근거가 부분적·애매하지만 코드에서 일부라도 뒷받침됨 — 통과시킴(이후 적대검증이 거른다).
173
- - rejected: 코드로 보아 '명백히 틀린' 경우, 또는 코드에서 근거가 전혀 확인되지 않고 반증신호가 우세한 경우. 단 코드에서 일부라도 뒷받침되면 기각하지 말고 uncertain(진짜 원인이면 코드에 근거가 남으므로, '근거 전무'만 기각해 누락을 막는다).
174
- - 판정 근거(reason)에 확인한 코드 위치·내용을 인용. 통과면 refinedCause 에 구체화된 원인을 적을 것.
175
-
176
- 2) 해결책(rejected 가 아닐 때만):
177
- - 검증하며 확인한 코드 근거 위에서, 근본 원인을 가장 직접적으로 제거하는 정도가 높은 순으로 정렬해 해결책 후보를 최대 2개, 서로 접근이 다르게.
178
- - 각 후보에 approach·mechanism(원인을 어떻게 제거하나)·changeScope(수정 범위) 채움.
179
- - 과도한 설계(over-engineering)·증상만 가리는 임시방편은 피할 것.
180
- - rejected 면 solutions 를 빈 배열로 둘 것.
181
-
182
- 가설:
183
- - 제목: ${h.title}
184
- - 원인: ${h.cause}
185
- - 관점: ${h.perspective}
186
- - 예상 근거: ${h.evidenceExpected}
187
- - 반증 신호: ${h.refuteSignal}`;
188
-
189
- const adversarialPrompt = (h, verdict, sol) => `문제: ${problem}
190
-
191
- ${PRINCIPLES}
192
-
193
- 너의 일: 아래 (원인 가설 + 해결책) 쌍을 다음 4개 관점 각각에서 적대적으로 공격하라. 기본 입장은 '이 해결책은 결함이 있다'로 두고 약점을 찾을 것. 각 관점을 서로 끌려가지 말고 독립적으로 판정해 lenses 배열로 반환(관점마다 1개 항목).
194
-
195
- 관점:
196
- ${LENSES.map((l) => `- ${l.title}: ${l.focus}`).join("\n")}
197
-
198
- 각 관점 판정:
199
- - pass=false 로 둘 결함을 찾으면 reason 에 코드 근거와 함께 적고, 치명적(회귀 유발·인과 불성립 등)이면 critical=true. 교정안이 있으면 revisedNote 에.
200
- - 그 관점에서 결함이 없으면 pass=true, critical=false, revisedNote="".
201
-
202
- 원인 가설: ${h.title} — ${verdict?.refinedCause || h.cause}
203
- 해결책: ${sol.approach} / ${sol.mechanism} (수정 범위: ${sol.changeScope})`;
204
-
205
- // ── [Hypothesize] 관점 생성 → 관점별 병렬 추출 + 자유탐색 → 중복제거 ──
206
- phase("Hypothesize");
207
-
208
- const perspectivePlan = await agent(
209
- `문제: ${problem}
210
-
211
- ${PRINCIPLES}
212
-
213
- 너의 일 (디버깅 1단계 — 의심 관점 도출). 위 증상을 보고, 원인을 찾을 때 서로 겹치지 않는 '의심 관점(범주)'을 도출하라.
214
- - 증상 성격에 맞춰 동적으로 고를 것. 예시 범주: ${PERSPECTIVE_EXAMPLES}. (예시일 뿐 — 증상에 맞게 가감)
215
- - 각 관점에 key, title, focus(이 관점이 의심하는 구체 지점)를 채울 것.
216
- - 보통 3~6개. 증상을 좁게 가리키면 적게, 막연하면 넓게.
217
- - 관점 선정 근거를 notes 에.`,
218
- { label: "perspectives", phase: "Hypothesize", schema: PERSPECTIVES_SCHEMA },
219
- );
220
-
221
- if (!perspectivePlan) throw new Error("[Hypothesize/perspectives] 관점 도출 에이전트 실행 실패(null) — 중단.");
222
- const PERSPECTIVES = (perspectivePlan.perspectives ?? []).filter(Boolean);
223
- if (PERSPECTIVES.length === 0) throw new Error("의심 관점을 도출하지 못했습니다.");
224
- log(`관점 ${PERSPECTIVES.length}개: ${PERSPECTIVES.map((p) => p.title).join(", ")}`);
225
-
226
- const extractTasks = PERSPECTIVES.map((p) => () =>
227
- agent(
228
- `문제: ${problem}
229
-
230
- ${PRINCIPLES}
231
-
232
- 너의 일: 오직 '${p.title}' 관점에서만 원인 가설을 발굴하라. 이 관점의 의심 지점: ${p.focus}
233
- - 코드베이스를 Grep/Glob/Read 로 직접 조사해 이 관점에 해당하는 원인 후보를 가능한 한 빠짐없이 뽑을 것(재현율 우선).
234
- - 다른 관점의 원인은 무시(중복은 이후 단계가 정리).
235
- - 각 가설에 title·cause·perspective('${p.title}')·evidenceExpected·refuteSignal 을 채울 것.
236
- - 해당 관점에서 원인이 안 보이면 hypotheses 를 비울 것(억지 생성 금지).`,
237
- { label: `extract:${p.key}`, phase: "Hypothesize", schema: HYPOTHESES_SCHEMA },
238
- ),
239
- );
240
-
241
- const freeTask = () =>
242
- agent(
243
- `문제: ${problem}
244
-
245
- ${PRINCIPLES}
246
-
247
- 너의 일: 어떤 정해진 관점에도 매이지 말고 자유롭게 원인 가설을 발굴하라(사각지대 안전망).
248
- - 앞서 정한 관점 목록(${PERSPECTIVES.map((p) => p.title).join(", ")})에 잘 안 들어가는 원인일수록 가치가 크다.
249
- - 코드베이스를 직접 조사해 근거 기반으로 뽑을 것. 각 가설에 title·cause·perspective('free')·evidenceExpected·refuteSignal 채움.`,
250
- { label: "extract:free", phase: "Hypothesize", schema: HYPOTHESES_SCHEMA },
251
- );
252
-
253
- const extracted = await parallel([...extractTasks, freeTask]);
254
- assertNoFailures(extracted, "Hypothesize/extract", [...PERSPECTIVES.map((p) => `extract:${p.key}`), "extract:free"]);
255
- const rawHypotheses = extracted.flatMap((r) => r.hypotheses ?? []);
256
- if (rawHypotheses.length === 0) {
257
- throw new Error("원인 가설을 하나도 도출하지 못했습니다. 문제 설명을 보강해 재호출하세요.");
258
- }
259
-
260
- const dedup = await agent(
261
- `아래는 여러 관점에서 발굴된 원인 가설들이다(JSON). 의미 기준으로 병합·중복제거하라.
262
-
263
- 병합 규칙:
264
- - '같은 근본 원인'을 가리키는 가설끼리만 하나로 합칠 것(표현만 다른 중복 제거). 근본 원인이 다르면 절대 합치지 말 것(서로 다른 원인을 뭉개면 검증에서 통째 탈락한다).
265
- - 병합 시 evidenceExpected·refuteSignal 은 합쳐 보존. perspective 는 합쳐진 관점들을 표기.
266
- - 개수를 인위적으로 줄이지 말 것(재현율 우선). 진짜 중복만 제거.
267
-
268
- 가설들(JSON):
269
- ${JSON.stringify(rawHypotheses)}`,
270
- { label: "dedup", phase: "Hypothesize", schema: HYPOTHESES_SCHEMA },
271
- );
272
-
273
- if (!dedup) throw new Error("[Hypothesize/dedup] 중복제거 에이전트 실행 실패(null) — 중단.");
274
- const HYPOTHESES = (dedup.hypotheses ?? []).filter(Boolean);
275
- if (HYPOTHESES.length === 0) throw new Error("중복제거 후 남은 가설이 없습니다.");
276
- log(`가설 ${HYPOTHESES.length}개 (원시 ${rawHypotheses.length}개에서 병합)`);
277
-
278
- // ── 비용 상한: 곱셈이 시스템 상한(1000) 근접 시 사전 throw ───────
279
- // 가설은 안 자른다(누락 방지). 전체 곱셈이 상한 근접하면 통째 throw 로 보고.
280
- const EST_PER_HYP = 1 /*verify+solve 통합*/ + 2 /*adversarial: 해결책 후보당 1(4관점 통합)*/;
281
- const EST_AGENTS = HYPOTHESES.length * EST_PER_HYP;
282
- const SAFE_AGENT_LIMIT = 900;
283
- if (EST_AGENTS > SAFE_AGENT_LIMIT) {
284
- throw new Error(
285
- `가설 ${HYPOTHESES.length}개 × 단계 ≈ ${EST_AGENTS} 에이전트로 시스템 상한(1000) 근접. 가설을 자르면 중요한 원인이 누락될 수 있으니, 문제 설명을 더 좁혀 재호출하세요.`,
286
- );
287
- }
288
-
289
- // ── [Verify(+Solve)]→[Adversarial] 가설별 파이프라인 (무배리어) ──
290
- phase("Verify");
291
- const piped = await pipeline(
292
- HYPOTHESES,
293
- // stage1: 검증+해결책 통합 (한 에이전트가 검증하며 읽은 코드로 바로 해결책까지)
294
- (h, _h, i) =>
295
- agent(verifySolvePrompt(h), { label: `diagnose:${i}`, phase: "Verify", schema: VERIFY_SOLVE_SCHEMA }).then((v) => ({
296
- index: i,
297
- hypothesis: h,
298
- verdict: { verdict: v.verdict, reason: v.reason, refinedCause: v.refinedCause },
299
- solutions: v.verdict === "rejected" ? [] : (v.solutions ?? []).slice(0, 2),
300
- })),
301
- // stage2: 적대검증 (해결책별 fan-out, 4관점은 단일 에이전트가 1회 통합 판정)
302
- (prev, h, i) => {
303
- // stage1 실패(null)는 복구하지 말고 그대로 전파 → line 320 assertNoFailures(piped) 가 포착(fail-fast).
304
- if (!prev) return null;
305
- // 여기부터 prev 는 비-null 보장 → verdict 보존됨(정상 무해결책만 빈 judged 로 통과).
306
- if (!prev.solutions || prev.solutions.length === 0) return Promise.resolve({ ...prev, judged: [] });
307
- return parallel(
308
- prev.solutions.map((sol, si) => () =>
309
- agent(adversarialPrompt(h, prev.verdict, sol), {
310
- label: `adv:${i}:${si}`,
311
- phase: "Adversarial",
312
- schema: ADVERSARIAL_SCHEMA,
313
- }).then((v) => ({ solution: sol, lensResults: (v?.lenses ?? []).filter(Boolean) })),
314
- ),
315
- ).then((judged) => {
316
- assertNoFailures(judged, `Adversarial(가설#${i})`, prev.solutions.map((_, si) => `adv:${i}:${si}`));
317
- return { ...prev, judged };
318
- });
319
- },
320
- );
321
-
322
- // ── 집계: 검증 기각 분리 + 해결책 veto+다수결 판정 ──────────────
323
- // fail-fast: stage1(diagnose) reject 또는 stage2(adversarial) 실패로 null 이 된 가설이 하나라도 있으면 중단.
324
- assertNoFailures(piped, "Verify/Adversarial", HYPOTHESES.map((h) => h.title));
325
- const results = piped;
326
- const droppedH = results.filter((x) => x.verdict?.verdict === "rejected");
327
- const survivedH = results.filter((x) => x.verdict && x.verdict.verdict !== "rejected");
328
-
329
- function judgeSolution(j) {
330
- const lr = (j.lensResults ?? []).filter(Boolean);
331
- const veto = lr.some((l) => l.critical === true && l.pass === false); // 치명결함 1표면 기각
332
- const passVotes = lr.filter((l) => l.pass === true).length;
333
- const total = lr.length;
334
- const passed = !veto && total > 0 && passVotes > total / 2; // veto 없으면 통과 다수결
335
- const risks = lr.filter((l) => l.pass === false).map((l) => `[${l.lens}] ${l.reason}`);
336
- const revisions = lr.filter((l) => l.revisedNote && l.revisedNote.trim() !== "").map((l) => `[${l.lens}] ${l.revisedNote}`);
337
- return { passed, veto, passVotes, total, risks, revisions };
338
- }
339
-
340
- const survivedDetailed = survivedH.map((x) => {
341
- const sols = (x.judged ?? []).map((j) => ({ solution: j.solution, ...judgeSolution(j) }));
342
- return {
343
- hypothesis: x.hypothesis,
344
- verdict: x.verdict.verdict,
345
- verifyReason: x.verdict.reason,
346
- refinedCause: x.verdict.refinedCause,
347
- solutions: sols,
348
- passedSolutions: sols.filter((s) => s.passed),
349
- };
350
- });
351
-
352
- const solutionsPassed = survivedDetailed.reduce((n, x) => n + x.passedSolutions.length, 0);
353
- const noSolution = solutionsPassed === 0;
354
-
355
- // 병합·우선순위화·렌더·결정 진행은 호출측(SKILL.md/메인 루프)이 수행.
356
- // 워크플로는 검증·적대검증 완료된 가설+해결책(평탄화) + 기각 가설을 구조화 반환.
357
- return {
358
- problem,
359
- perspectives: PERSPECTIVES.map((p) => p.title),
360
- summary: {
361
- hypotheses: HYPOTHESES.length,
362
- confirmed: survivedH.filter((x) => x.verdict.verdict === "confirmed").length,
363
- uncertain: survivedH.filter((x) => x.verdict.verdict === "uncertain").length,
364
- dropped: droppedH.length,
365
- solutionsPassed,
366
- noSolution,
367
- },
368
- survived: survivedDetailed.map((x) => ({
369
- hypothesis: x.hypothesis.title,
370
- cause: x.refinedCause || x.hypothesis.cause,
371
- perspective: x.hypothesis.perspective,
372
- verdict: x.verdict,
373
- verifyReason: x.verifyReason,
374
- solutions: x.solutions.map((s) => ({
375
- approach: s.solution.approach,
376
- mechanism: s.solution.mechanism,
377
- changeScope: s.solution.changeScope,
378
- passed: s.passed,
379
- vetoed: s.veto,
380
- votes: `${s.passVotes}/${s.total}`,
381
- risks: s.risks,
382
- revisions: s.revisions,
383
- })),
384
- })),
385
- dropped: droppedH.map((x) => ({
386
- hypothesis: x.hypothesis.title,
387
- cause: x.hypothesis.cause,
388
- reason: x.verdict.reason,
389
- })),
390
- };