@tienne/gestalt 0.84.1 → 0.85.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/CLAUDE.md +4 -4
  2. package/README.ko.md +72 -22
  3. package/README.md +73 -23
  4. package/dist/benchmarks/runners/passthrough-benchmark-runner.d.ts.map +1 -1
  5. package/dist/benchmarks/runners/passthrough-benchmark-runner.js +8 -7
  6. package/dist/benchmarks/runners/passthrough-benchmark-runner.js.map +1 -1
  7. package/dist/bin/gestalt.js +2 -2
  8. package/dist/package.json +1 -1
  9. package/dist/plugin/skills/dispatch/SKILL.md +14 -2
  10. package/dist/plugin/skills/execute/SKILL.md +219 -52
  11. package/dist/plugin/skills/interview/SKILL.md +15 -2
  12. package/dist/plugin/skills/review/SKILL.md +29 -5
  13. package/dist/plugin/skills/solve/SKILL.md +20 -5
  14. package/dist/plugin/skills/spec/SKILL.md +2 -0
  15. package/dist/src/cli/commands/interview.d.ts.map +1 -1
  16. package/dist/src/cli/commands/interview.js +16 -4
  17. package/dist/src/cli/commands/interview.js.map +1 -1
  18. package/dist/src/cli/commands/memory-merge.d.ts +2 -0
  19. package/dist/src/cli/commands/memory-merge.d.ts.map +1 -0
  20. package/dist/src/cli/commands/memory-merge.js +13 -0
  21. package/dist/src/cli/commands/memory-merge.js.map +1 -0
  22. package/dist/src/cli/index.d.ts.map +1 -1
  23. package/dist/src/cli/index.js +7 -0
  24. package/dist/src/cli/index.js.map +1 -1
  25. package/dist/src/core/constants.d.ts +4 -0
  26. package/dist/src/core/constants.d.ts.map +1 -1
  27. package/dist/src/core/constants.js +8 -0
  28. package/dist/src/core/constants.js.map +1 -1
  29. package/dist/src/core/errors.d.ts +3 -0
  30. package/dist/src/core/errors.d.ts.map +1 -1
  31. package/dist/src/core/errors.js +7 -0
  32. package/dist/src/core/errors.js.map +1 -1
  33. package/dist/src/core/file-lock.d.ts +37 -0
  34. package/dist/src/core/file-lock.d.ts.map +1 -0
  35. package/dist/src/core/file-lock.js +143 -0
  36. package/dist/src/core/file-lock.js.map +1 -0
  37. package/dist/src/core/json-file.d.ts +40 -0
  38. package/dist/src/core/json-file.d.ts.map +1 -0
  39. package/dist/src/core/json-file.js +82 -0
  40. package/dist/src/core/json-file.js.map +1 -0
  41. package/dist/src/core/types.d.ts +86 -9
  42. package/dist/src/core/types.d.ts.map +1 -1
  43. package/dist/src/events/store.d.ts +6 -0
  44. package/dist/src/events/store.d.ts.map +1 -1
  45. package/dist/src/events/store.js +12 -0
  46. package/dist/src/events/store.js.map +1 -1
  47. package/dist/src/events/types.d.ts +7 -0
  48. package/dist/src/events/types.d.ts.map +1 -1
  49. package/dist/src/events/types.js +7 -0
  50. package/dist/src/events/types.js.map +1 -1
  51. package/dist/src/execute/artifact-verifier.d.ts +12 -0
  52. package/dist/src/execute/artifact-verifier.d.ts.map +1 -0
  53. package/dist/src/execute/artifact-verifier.js +211 -0
  54. package/dist/src/execute/artifact-verifier.js.map +1 -0
  55. package/dist/src/execute/drift-detector.d.ts +2 -0
  56. package/dist/src/execute/drift-detector.d.ts.map +1 -1
  57. package/dist/src/execute/drift-detector.js +9 -13
  58. package/dist/src/execute/drift-detector.js.map +1 -1
  59. package/dist/src/execute/orchestrators/evaluation.d.ts +5 -1
  60. package/dist/src/execute/orchestrators/evaluation.d.ts.map +1 -1
  61. package/dist/src/execute/orchestrators/evaluation.js +48 -41
  62. package/dist/src/execute/orchestrators/evaluation.js.map +1 -1
  63. package/dist/src/execute/orchestrators/evolution.d.ts +17 -3
  64. package/dist/src/execute/orchestrators/evolution.d.ts.map +1 -1
  65. package/dist/src/execute/orchestrators/evolution.js +102 -53
  66. package/dist/src/execute/orchestrators/evolution.js.map +1 -1
  67. package/dist/src/execute/orchestrators/execution.d.ts +17 -2
  68. package/dist/src/execute/orchestrators/execution.d.ts.map +1 -1
  69. package/dist/src/execute/orchestrators/execution.js +99 -3
  70. package/dist/src/execute/orchestrators/execution.js.map +1 -1
  71. package/dist/src/execute/orchestrators/planning.d.ts +18 -4
  72. package/dist/src/execute/orchestrators/planning.d.ts.map +1 -1
  73. package/dist/src/execute/orchestrators/planning.js +62 -21
  74. package/dist/src/execute/orchestrators/planning.js.map +1 -1
  75. package/dist/src/execute/orchestrators/types.d.ts +12 -1
  76. package/dist/src/execute/orchestrators/types.d.ts.map +1 -1
  77. package/dist/src/execute/passthrough-engine.d.ts +12 -5
  78. package/dist/src/execute/passthrough-engine.d.ts.map +1 -1
  79. package/dist/src/execute/passthrough-engine.js +10 -4
  80. package/dist/src/execute/passthrough-engine.js.map +1 -1
  81. package/dist/src/execute/repository.d.ts +11 -1
  82. package/dist/src/execute/repository.d.ts.map +1 -1
  83. package/dist/src/execute/repository.js +138 -19
  84. package/dist/src/execute/repository.js.map +1 -1
  85. package/dist/src/execute/session.d.ts +26 -4
  86. package/dist/src/execute/session.d.ts.map +1 -1
  87. package/dist/src/execute/session.js +165 -48
  88. package/dist/src/execute/session.js.map +1 -1
  89. package/dist/src/execute/structural-commands.d.ts +23 -0
  90. package/dist/src/execute/structural-commands.d.ts.map +1 -0
  91. package/dist/src/execute/structural-commands.js +139 -0
  92. package/dist/src/execute/structural-commands.js.map +1 -0
  93. package/dist/src/interview/engine.d.ts +1 -1
  94. package/dist/src/interview/engine.d.ts.map +1 -1
  95. package/dist/src/interview/engine.js +3 -3
  96. package/dist/src/interview/engine.js.map +1 -1
  97. package/dist/src/interview/passthrough-engine.d.ts +1 -1
  98. package/dist/src/interview/passthrough-engine.d.ts.map +1 -1
  99. package/dist/src/interview/passthrough-engine.js +20 -3
  100. package/dist/src/interview/passthrough-engine.js.map +1 -1
  101. package/dist/src/interview/repository.d.ts +8 -0
  102. package/dist/src/interview/repository.d.ts.map +1 -1
  103. package/dist/src/interview/repository.js +16 -1
  104. package/dist/src/interview/repository.js.map +1 -1
  105. package/dist/src/interview/session.d.ts +15 -2
  106. package/dist/src/interview/session.d.ts.map +1 -1
  107. package/dist/src/interview/session.js +67 -23
  108. package/dist/src/interview/session.js.map +1 -1
  109. package/dist/src/knowledge-base/embedding.d.ts +6 -1
  110. package/dist/src/knowledge-base/embedding.d.ts.map +1 -1
  111. package/dist/src/knowledge-base/embedding.js +19 -6
  112. package/dist/src/knowledge-base/embedding.js.map +1 -1
  113. package/dist/src/local-pr/registry.d.ts +1 -21
  114. package/dist/src/local-pr/registry.d.ts.map +1 -1
  115. package/dist/src/local-pr/registry.js +7 -113
  116. package/dist/src/local-pr/registry.js.map +1 -1
  117. package/dist/src/mcp/input-guard.d.ts +17 -0
  118. package/dist/src/mcp/input-guard.d.ts.map +1 -1
  119. package/dist/src/mcp/input-guard.js +1 -1
  120. package/dist/src/mcp/input-guard.js.map +1 -1
  121. package/dist/src/mcp/schemas.d.ts +62 -7
  122. package/dist/src/mcp/schemas.d.ts.map +1 -1
  123. package/dist/src/mcp/schemas.js +28 -1
  124. package/dist/src/mcp/schemas.js.map +1 -1
  125. package/dist/src/mcp/server.d.ts.map +1 -1
  126. package/dist/src/mcp/server.js +26 -40
  127. package/dist/src/mcp/server.js.map +1 -1
  128. package/dist/src/mcp/tools/execute/evaluate.d.ts.map +1 -1
  129. package/dist/src/mcp/tools/execute/evaluate.js +15 -9
  130. package/dist/src/mcp/tools/execute/evaluate.js.map +1 -1
  131. package/dist/src/mcp/tools/execute/evolve.d.ts +1 -0
  132. package/dist/src/mcp/tools/execute/evolve.d.ts.map +1 -1
  133. package/dist/src/mcp/tools/execute/evolve.js +102 -37
  134. package/dist/src/mcp/tools/execute/evolve.js.map +1 -1
  135. package/dist/src/mcp/tools/execute/execution.d.ts.map +1 -1
  136. package/dist/src/mcp/tools/execute/execution.js +66 -7
  137. package/dist/src/mcp/tools/execute/execution.js.map +1 -1
  138. package/dist/src/mcp/tools/execute/planning.d.ts.map +1 -1
  139. package/dist/src/mcp/tools/execute/planning.js +34 -2
  140. package/dist/src/mcp/tools/execute/planning.js.map +1 -1
  141. package/dist/src/mcp/tools/execute/roles.d.ts.map +1 -1
  142. package/dist/src/mcp/tools/execute/roles.js +13 -5
  143. package/dist/src/mcp/tools/execute/roles.js.map +1 -1
  144. package/dist/src/mcp/tools/execute/utility.d.ts.map +1 -1
  145. package/dist/src/mcp/tools/execute/utility.js +26 -0
  146. package/dist/src/mcp/tools/execute/utility.js.map +1 -1
  147. package/dist/src/mcp/tools/execute/utils.d.ts +1 -0
  148. package/dist/src/mcp/tools/execute/utils.d.ts.map +1 -1
  149. package/dist/src/mcp/tools/execute/utils.js +15 -0
  150. package/dist/src/mcp/tools/execute/utils.js.map +1 -1
  151. package/dist/src/mcp/tools/execute-passthrough.d.ts.map +1 -1
  152. package/dist/src/mcp/tools/execute-passthrough.js +21 -1
  153. package/dist/src/mcp/tools/execute-passthrough.js.map +1 -1
  154. package/dist/src/mcp/tools/interview-passthrough.d.ts +2 -1
  155. package/dist/src/mcp/tools/interview-passthrough.d.ts.map +1 -1
  156. package/dist/src/mcp/tools/interview-passthrough.js +4 -3
  157. package/dist/src/mcp/tools/interview-passthrough.js.map +1 -1
  158. package/dist/src/mcp/tools/interview.d.ts.map +1 -1
  159. package/dist/src/mcp/tools/interview.js +2 -1
  160. package/dist/src/mcp/tools/interview.js.map +1 -1
  161. package/dist/src/mcp/tools/review-passthrough.js +15 -19
  162. package/dist/src/mcp/tools/review-passthrough.js.map +1 -1
  163. package/dist/src/mcp/tools/spec-passthrough.d.ts.map +1 -1
  164. package/dist/src/mcp/tools/spec-passthrough.js +16 -8
  165. package/dist/src/mcp/tools/spec-passthrough.js.map +1 -1
  166. package/dist/src/mcp/tools/status.d.ts.map +1 -1
  167. package/dist/src/mcp/tools/status.js +4 -0
  168. package/dist/src/mcp/tools/status.js.map +1 -1
  169. package/dist/src/memory/memory-context-injector.d.ts +22 -8
  170. package/dist/src/memory/memory-context-injector.d.ts.map +1 -1
  171. package/dist/src/memory/memory-context-injector.js +85 -13
  172. package/dist/src/memory/memory-context-injector.js.map +1 -1
  173. package/dist/src/memory/memory-merge.d.ts +19 -0
  174. package/dist/src/memory/memory-merge.d.ts.map +1 -0
  175. package/dist/src/memory/memory-merge.js +120 -0
  176. package/dist/src/memory/memory-merge.js.map +1 -0
  177. package/dist/src/memory/project-memory-store.d.ts +15 -11
  178. package/dist/src/memory/project-memory-store.d.ts.map +1 -1
  179. package/dist/src/memory/project-memory-store.js +97 -120
  180. package/dist/src/memory/project-memory-store.js.map +1 -1
  181. package/dist/src/memory/semantic-search.d.ts +4 -1
  182. package/dist/src/memory/semantic-search.d.ts.map +1 -1
  183. package/dist/src/memory/semantic-search.js +66 -19
  184. package/dist/src/memory/semantic-search.js.map +1 -1
  185. package/dist/src/resilience/lateral.d.ts +8 -1
  186. package/dist/src/resilience/lateral.d.ts.map +1 -1
  187. package/dist/src/resilience/lateral.js +52 -4
  188. package/dist/src/resilience/lateral.js.map +1 -1
  189. package/package.json +1 -1
  190. package/plugin/.codex-plugin/plugin.json +1 -1
  191. package/plugin/.mcp.json +1 -1
  192. package/plugin/mcp.json +1 -1
  193. package/plugin/skills/dispatch/SKILL.md +14 -2
  194. package/plugin/skills/execute/SKILL.md +219 -52
  195. package/plugin/skills/interview/SKILL.md +15 -2
  196. package/plugin/skills/review/SKILL.md +29 -5
  197. package/plugin/skills/solve/SKILL.md +20 -5
  198. package/plugin/skills/spec/SKILL.md +2 -0
  199. package/dist/src/interview/mini-interview-engine.d.ts +0 -33
  200. package/dist/src/interview/mini-interview-engine.d.ts.map +0 -1
  201. package/dist/src/interview/mini-interview-engine.js +0 -102
  202. package/dist/src/interview/mini-interview-engine.js.map +0 -1
  203. package/dist/src/memory/user-profile-store.d.ts +0 -13
  204. package/dist/src/memory/user-profile-store.d.ts.map +0 -1
  205. package/dist/src/memory/user-profile-store.js +0 -85
  206. package/dist/src/memory/user-profile-store.js.map +0 -1
@@ -1,16 +1,16 @@
1
1
  ---
2
2
  name: execute
3
- version: "1.3.0"
4
- description: "Gestalt-driven execution planner that transforms a Spec into a validated ExecutionPlan. Requires a Spec that already exists. Given only a problem statement, use solve instead — it drives interview → spec → execute as one loop."
3
+ version: '1.3.0'
4
+ description: 'Gestalt-driven execution planner that transforms a Spec into a validated ExecutionPlan. Requires a Spec that already exists. Given only a problem statement, use solve instead — it drives interview → spec → execute as one loop.'
5
5
  triggers:
6
- - "execute"
7
- - "plan execution"
8
- - "create execution plan"
6
+ - 'execute'
7
+ - 'plan execution'
8
+ - 'create execution plan'
9
9
  inputs:
10
10
  spec:
11
11
  type: object
12
12
  required: true
13
- description: "A validated Spec specification from the spec generation step"
13
+ description: 'A validated Spec specification from the spec generation step'
14
14
  outputs:
15
15
  - executionPlan
16
16
  ---
@@ -61,6 +61,7 @@ Success condition: `score ≥ 0.85` AND `goalAlignment ≥ 0.80`
61
61
  - **Flow A — Structural Fix**: fix lint/build/test failures → re-evaluate
62
62
  - **Flow B — Contextual Evolution**: patch Spec ACs/constraints → re-execute impacted tasks → re-evaluate
63
63
  - **Flow C — Lateral Thinking**: when stagnation detected, rotate through Multistability / Simplicity / Reification / Invariance personas
64
+ - **Flow D — Human Decision**: when all four personas are exhausted, the session pauses as `awaiting_human` — ask the human, then `gate_resolve` and follow `nextAction`
64
65
 
65
66
  ## Phase 0 — 규칙 확보 (필수 — 스킵 불가)
66
67
 
@@ -109,16 +110,15 @@ ges_status() → {
109
110
 
110
111
  **`ruleSourceErrors`가 비어 있지 않으면 멈춘다.** 판정과 사용자에게 알릴 문구는 [`../_shared/rule-sources.md`](../_shared/rule-sources.md)의 "선언이 깨졌을 때" 절이 원본이다. **`ruleSources`가 비어 있지 않아도 일부가 빠진 상태일 수 있으니 배열 길이로 판정하지 않는다.**
111
112
 
112
-
113
113
  `scope`가 비어 있지 않으면 **이번 Spec에 해당하는 소스만** 읽는다. 백엔드 태스크만 있는 Spec에서 디자인 토큰을 물어볼 이유가 없다. 여기는 건드릴 파일이 정해진 자리라 태그를 그 파일에서 뽑는다.
114
114
 
115
115
  **`solve`로 들어와 소스 읽기를 건너뛰는 경우에도 이 판정은 한다.** 건너뛰는 건 읽기지 판정이 아니다. 앞 스킬은 파일을 모른 채 넘겼으므로 여기서 세 가지가 정해진다.
116
116
 
117
- | 앞에서 넘어온 것 | 여기서 |
118
- |---|---|
117
+ | 앞에서 넘어온 것 | 여기서 |
118
+ | -------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
119
119
  | 넓게 잡아 읽어둔 `convention` 소스 중 파일을 보니 안 걸리는 것 | `repoRules.sources`에서 뺀다. 거기서 빠지면 서브에이전트 프롬프트와 완료 보고 양쪽에서 함께 빠진다 |
120
- | 판정을 미뤄둔 `stop`과 `delegate` 소스 | 여기서 판정한다. 걸리면 읽거나 멈추거나 넘긴다 |
121
- | 새로 걸리는 소스 | 그것만 더 읽는다 |
120
+ | 판정을 미뤄둔 `stop`과 `delegate` 소스 | 여기서 판정한다. 걸리면 읽거나 멈추거나 넘긴다 |
121
+ | 새로 걸리는 소스 | 그것만 더 읽는다 |
122
122
 
123
123
  `trust: "delegate"`인 소스가 이번 작업 범위에 걸리면 그 부분은 게슈탈트가 직접 만들지 않고 넘긴다. 어디까지 넘기는지 사용자에게 알리고 진행한다.
124
124
 
@@ -145,41 +145,52 @@ API 키 없이 MCP 서버 실행 시 자동 활성화. LLM 작업을 caller가
145
145
  ### Action별 사용법
146
146
 
147
147
  **`start`** — 실행 계획 세션 시작
148
+
148
149
  ```json
149
150
  { "action": "start", "spec": { ... } }
150
151
  ```
152
+
151
153
  → `{ status, sessionId, specId, executeContext, message }`
152
154
 
153
155
  **`plan_step`** — 각 계획 단계 결과 제출
156
+
154
157
  ```json
155
158
  { "action": "plan_step", "sessionId": "...", "stepResult": { "principle": "figure_ground", "classifiedACs": [...] } }
156
159
  ```
160
+
157
161
  → `{ status, sessionId, stepsCompleted, isLastStep, executeContext?, message }`
158
162
 
163
+ continuity에서 DAG가 무효면 `error`가 함께 실린 `status: "plan_rewound"` 응답이 온다. [DAG가 무효일 때](#dag가-무효일-때--closure로-되감기)를 본다.
164
+
159
165
  **`plan_complete`** — 최종 실행 계획 조립
166
+
160
167
  ```json
161
168
  { "action": "plan_complete", "sessionId": "..." }
162
169
  ```
170
+
163
171
  → `{ status, sessionId, executionPlan, message }`
164
172
 
173
+ 저장된 계획의 DAG가 무효면 확정하지 않고 같은 `plan_rewound` 응답을 돌려준다.
174
+
165
175
  **`status`** — 세션 상태 확인
176
+
166
177
  ```json
167
178
  { "action": "status", "sessionId": "..." }
168
179
  ```
169
180
 
170
181
  ### ExecuteContext 필드
171
182
 
172
- | 필드 | 타입 | 설명 |
173
- |------|------|------|
174
- | `systemPrompt` | string | 실행 계획 시스템 프롬프트 |
175
- | `planningPrompt` | string | 현재 단계의 계획 프롬프트 |
176
- | `currentPrinciple` | string | 현재 적용 중인 게슈탈트 원리 |
177
- | `principleStrategy` | string | 해당 원리의 전략 설명 |
178
- | `phase` | string | 현재 단계 (`planning`) |
179
- | `stepNumber` | number | 현재 스텝 번호 (1-4) |
180
- | `totalSteps` | number | 전체 스텝 수 (4) |
181
- | `spec` | Spec | 원본 Spec 스펙 |
182
- | `previousSteps` | array | 이전 단계 결과들 |
183
+ | 필드 | 타입 | 설명 |
184
+ | ------------------- | ------ | ----------------------------------------------------------------------------------------------- |
185
+ | `systemPrompt` | string | 실행 계획 시스템 프롬프트 |
186
+ | `planningPrompt` | string | 현재 단계의 계획 프롬프트 |
187
+ | `currentStage` | string | 현재 단계를 풀어 쓴 문구 (예: `빠진 요구사항 채우기`). 서버 내부의 원리 이름 대신 응답에 실린다 |
188
+ | `principleStrategy` | string | 해당 원리의 전략 설명 |
189
+ | `phase` | string | 현재 단계 (`planning`) |
190
+ | `stepNumber` | number | 현재 스텝 번호 (1-4) |
191
+ | `totalSteps` | number | 전체 스텝 수 (4) |
192
+ | `spec` | Spec | 원본 Spec 스펙 |
193
+ | `previousSteps` | array | 이전 단계 결과들 |
183
194
 
184
195
  ### Planning Principle 순서
185
196
 
@@ -195,6 +206,31 @@ API 키 없이 MCP 서버 실행 시 자동 활성화. LLM 작업을 caller가
195
206
  - Continuity 단계에서는 서버 측 DAG 검증이 추가로 수행됨
196
207
  - 모든 AC가 분류되어야 하고 모든 Task가 그룹에 포함되어야 함
197
208
 
209
+ ### DAG가 무효일 때 — closure로 되감기
210
+
211
+ 순환은 closure 단계 `atomicTasks`의 `dependsOn`에서 생긴다. continuity만 다시 내서는 못 고친다. 그래서 `dagValidation.isValid: false`를 보고하거나 서버가 순환이나 충돌을 찾으면 `plan_step`(continuity)과 `plan_complete` 둘 다 계획을 확정하지 않는다. 대신 세션을 figure_ground까지만 남기고 되감은 뒤 이렇게 돌려준다.
212
+
213
+ ```json
214
+ {
215
+ "error": "Dependency DAG is invalid (...)",
216
+ "status": "plan_rewound",
217
+ "sessionId": "...",
218
+ "rewoundToStep": 2,
219
+ "hasCycles": true,
220
+ "cycleDetails": ["Cycle detected involving tasks: task-0 → task-1"],
221
+ "conflictDetails": [],
222
+ "executeContext": { "currentStage": "빠진 요구사항 채우기", "stepNumber": 2, "...": "..." },
223
+ "nextAction": "plan_step"
224
+ }
225
+ ```
226
+
227
+ - `cycleDetails`와 `conflictDetails`를 보고 문제가 된 태스크의 `dependsOn`을 고친다.
228
+ - 응답의 `executeContext`로 closure부터 다시 낸다. 그다음 proximity와 continuity를 차례로 다시 제출한다.
229
+ - `isValid: false`를 그대로 두고 continuity만 다시 내면 또 되감긴다. 호출자 보고가 무효여도 막는다.
230
+ - 이 응답에는 `error` 필드가 함께 실린다. `plan_complete`나 `execute_start`로 넘어가지 않는다.
231
+ - 되감은 뒤의 closure 프롬프트에는 순환 정보도 직전 `atomicTasks`도 없다. 서브에이전트로 다시 낼 때는 응답의 `cycleDetails`와 `conflictDetails`, 직전에 제출한 `atomicTasks`를 함께 넘기고 순환을 만든 `dependsOn`을 고치라고 적는다.
232
+ - 같은 세션에서 `plan_rewound`를 연속 두 번 받으면 closure를 다시 제출하지 않는다. `cycleDetails`를 사용자에게 보여주고 어떻게 할지 묻는다. 서버는 횟수를 세지 않으므로 스킬이 센다.
233
+
198
234
  ### Reasoning Model 서브에이전트로 플래닝 추론
199
235
 
200
236
  Phase 1 플래닝(`plan_step` 4단계 + `plan_complete`)은 Spec을 태스크 DAG로 분해하는 깊은 one-shot 추론이라, 게슈탈트에서 상위 추론 모델이 진짜 값을 하는 지점이다. 각 `plan_step`의 `stepResult`(classifiedACs / atomicTasks / taskGroups / dagValidation)를 만드는 추론은 현재 세션이 직접 하지 말고 별도 Agent 서브에이전트로 스폰한다.
@@ -207,7 +243,7 @@ Phase 1 플래닝(`plan_step` 4단계 + `plan_complete`)은 Spec을 태스크 DA
207
243
  ges_status() → { reasoningModel: "fable", reasoningModelFallback: "opus", ... }
208
244
  ```
209
245
 
210
- **스폰.** 각 `plan_step`(그리고 `plan_complete` 조립을 위한 추론이 필요하면 그 단계)에서 Agent 도구로 서브에이전트를 띄우되 `model` 파라미터에 `reasoningModel` 값을 넘긴다. 서브에이전트에는 해당 단계의 `executeContext`(`systemPrompt`, `planningPrompt`, `currentPrinciple`, `spec`, `previousSteps`)를 전달하고 그 단계의 `stepResult`를 산출하게 한다. 결과를 `plan_step`으로 제출한다.
246
+ **스폰.** 각 `plan_step`(그리고 `plan_complete` 조립을 위한 추론이 필요하면 그 단계)에서 Agent 도구로 서브에이전트를 띄우되 `model` 파라미터에 `reasoningModel` 값을 넘긴다. 서브에이전트에는 해당 단계의 `executeContext`(`systemPrompt`, `planningPrompt`, `currentStage`, `spec`, `previousSteps`)를 전달하고 그 단계의 `stepResult`를 산출하게 한다. 결과를 `plan_step`으로 제출한다.
211
247
 
212
248
  **폴백은 스킬 런타임에서 발동한다.** 서버는 폴백 대상(`reasoningModelFallback`)만 알려줄 뿐, 모델 가용성을 감지하거나 재시도하지 않는다. Agent 도구가 `reasoningModel`(예: `fable`)을 지원하지 않아 스폰이 거부/실패하면, 그때 스킬이 직접 `model`을 `reasoningModelFallback`(예: `opus`)로 바꿔 1회 재시도한다. 폴백 판단과 재시도는 전적으로 이 스킬 런타임의 책임이다.
213
249
 
@@ -220,9 +256,18 @@ ges_status() → { reasoningModel: "fable", reasoningModelFallback: "opus", ..
220
256
  `plan_complete` 이후 호출. 태스크 목록을 받아 실행 준비.
221
257
 
222
258
  ```json
223
- { "action": "execute_start", "sessionId": "..." }
259
+ { "action": "execute_start", "sessionId": "...", "cwd": "/path/to/project" }
224
260
  ```
225
- → `{ status, sessionId, executionPlan, message }`
261
+
262
+ → `{ status, sessionId, executionPlan, artifactCheck, message }`
263
+
264
+ `cwd`에는 프로젝트 루트를 넘긴다. 서버가 이 디렉토리에서 git 작업 트리가 실행 시작 시점에 어떤 상태였는지 잡아 두고 나중에 `completed` 보고의 `artifacts`를 그 상태와 대조한다. `cwd`를 비우면 세션의 `codeGraphRepoRoot`를 쓰고 그것도 없으면 서버 프로세스의 cwd를 쓴다. git 레포가 아니면 대조 없이 진행한다. 대조 중에는 그 레포의 `filter.*.clean` 설정이 실행되므로 믿지 않는 레포를 `cwd`로 넘기지 않는다.
265
+
266
+ 응답에는 `artifactCheck: { repoRoot, baseline, error? }`가 붙는다. `repoRoot`는 실제로 대조에 쓸 디렉토리다. `baseline`은 아래 셋 중 하나다. `execute_task` 응답의 `artifactCheck`는 `verified` 같은 문자열 하나라 꼴이 다르다.
267
+
268
+ - `captured`: 시작 시점 상태를 잡았다. 이후 `completed` 보고를 대조한다
269
+ - `no_baseline`: git 레포가 아니라 대조하지 않는다
270
+ - `baseline_failed`: git 호출이 실패해 대조하지 않는다. 실패 내용은 `error`에 담긴다
226
271
 
227
272
  ---
228
273
 
@@ -236,6 +281,7 @@ ges_status() → { reasoningModel: "fable", reasoningModelFallback: "opus", ..
236
281
  // Call 1: 매칭 컨텍스트 요청
237
282
  { "action": "role_match", "sessionId": "..." }
238
283
  ```
284
+
239
285
  → `{ matchContext }` — 어떤 에이전트가 적합한지 판단하기 위한 프롬프트
240
286
 
241
287
  `matchContext.tierHint`는 `"frugal"`이다. `matchContext.availableAgents`에는 에이전트 20여 개의 description이 통째로 들어 있다. 그걸 세션 컨텍스트에 들이는 대신 **서브에이전트에 넘겨 1차 후보를 좁힌다.**
@@ -259,11 +305,22 @@ Agent {
259
305
  "action": "role_match",
260
306
  "sessionId": "...",
261
307
  "matchResult": [
262
- { "agentName": "technical-writer", "domain": ["documentation"], "relevanceScore": 0.9, "reasoning": "..." },
263
- { "agentName": "architect", "domain": ["architecture"], "relevanceScore": 0.7, "reasoning": "..." }
308
+ {
309
+ "agentName": "technical-writer",
310
+ "domain": ["documentation"],
311
+ "relevanceScore": 0.9,
312
+ "reasoning": "..."
313
+ },
314
+ {
315
+ "agentName": "architect",
316
+ "domain": ["architecture"],
317
+ "relevanceScore": 0.7,
318
+ "reasoning": "..."
319
+ }
264
320
  ]
265
321
  }
266
322
  ```
323
+
267
324
  → `{ perspectivePrompts }` — 각 에이전트별 관점 생성 프롬프트
268
325
 
269
326
  **`role_consensus` — 다중 관점 합의 (2-Call)**
@@ -279,6 +336,7 @@ Agent {
279
336
  ]
280
337
  }
281
338
  ```
339
+
282
340
  → `{ synthesisContext }` — 관점 통합 프롬프트
283
341
 
284
342
  ```json
@@ -293,6 +351,7 @@ Agent {
293
351
  }
294
352
  }
295
353
  ```
354
+
296
355
  → `{ roleGuidance }` — execute_task 시 참조할 최종 guidance
297
356
 
298
357
  ---
@@ -320,13 +379,13 @@ Agent {
320
379
 
321
380
  ```
322
381
  // 같은 메시지에서 동시 호출 — Agent 1
323
- Agent(task: "task-0 실행: {task-0 title}\n컨텍스트: {taskContext}\n완료 후 execute_task로 결과 제출")
382
+ Agent(task: "task-0 실행: {task-0 title}\n컨텍스트: {taskContext}\n완료 후 execute_task로 결과 제출. 바꾼 파일은 전부 artifacts에 적는다. 파일을 안 바꾸는 태스크면 noCodeChange: true를 같이 낸다. verification_failed가 오면 고쳐서 다시 제출하고 끝내지 못했으면 status: \"failed\"로 낸다. serverError: true가 함께 오면 고치지 말고 잠시 뒤 같은 결과를 다시 내고 계속 실패하면 status: \"failed\"로 낸다")
324
383
 
325
384
  // 같은 메시지에서 동시 호출 — Agent 2
326
- Agent(task: "task-1 실행: {task-1 title}\n컨텍스트: {taskContext}\n완료 후 execute_task로 결과 제출")
385
+ Agent(task: "task-1 실행: {task-1 title}\n컨텍스트: {taskContext}\n완료 후 execute_task로 결과 제출. 바꾼 파일은 전부 artifacts에 적는다. 파일을 안 바꾸는 태스크면 noCodeChange: true를 같이 낸다. verification_failed가 오면 고쳐서 다시 제출하고 끝내지 못했으면 status: \"failed\"로 낸다. serverError: true가 함께 오면 고치지 말고 잠시 뒤 같은 결과를 다시 내고 계속 실패하면 status: \"failed\"로 낸다")
327
386
 
328
387
  // 같은 메시지에서 동시 호출 — Agent 3
329
- Agent(task: "task-2 실행: {task-2 title}\n컨텍스트: {taskContext}\n완료 후 execute_task로 결과 제출")
388
+ Agent(task: "task-2 실행: {task-2 title}\n컨텍스트: {taskContext}\n완료 후 execute_task로 결과 제출. 바꾼 파일은 전부 artifacts에 적는다. 파일을 안 바꾸는 태스크면 noCodeChange: true를 같이 낸다. verification_failed가 오면 고쳐서 다시 제출하고 끝내지 못했으면 status: \"failed\"로 낸다. serverError: true가 함께 오면 고치지 말고 잠시 뒤 같은 결과를 다시 내고 계속 실패하면 status: \"failed\"로 낸다")
330
389
  ```
331
390
 
332
391
  **각 Agent의 execute_task 제출:**
@@ -345,6 +404,7 @@ Agent(task: "task-2 실행: {task-2 title}\n컨텍스트: {taskContext}\n완료
345
404
  ```
346
405
 
347
406
  실패 시:
407
+
348
408
  ```json
349
409
  {
350
410
  "action": "execute_task",
@@ -379,10 +439,54 @@ role_match/role_consensus로 얻은 `roleGuidance`를 참조해 태스크를 수
379
439
  }
380
440
  }
381
441
  ```
382
- → `{ status, nextTaskId?, allTasksCompleted, driftResult? }`
442
+
443
+ → `{ status, nextTaskId?, allTasksCompleted, artifactCheck?, driftResult? }`
383
444
 
384
445
  `driftResult`가 반환되면 Spec과의 drift 경고 — 계속 진행하되 다음 태스크에서 방향 보정.
385
446
 
447
+ **artifacts 대조.** `status: "completed"`로 보고하면 서버가 `artifacts`의 파일이 실행 시작 뒤로 실제로 바뀌었는지 확인한다. 수정하거나 새로 만들거나 커밋하거나 지운 파일은 모두 바뀐 것으로 친다. 상대 경로는 `execute_start`의 `cwd` 기준이다. 디렉토리나 gitignore에 걸린 경로는 확인할 수 없으니 바꾼 소스 파일을 하나씩 적는다. `failed`와 `skipped`는 대조하지 않는다. 대조는 `execute_task`에만 있고 `evolve_re_execute`와 `evolve_fix`로 낸 결과는 대조하지 않는다.
448
+
449
+ `artifacts`가 빈 `completed`는 거절된다. 조사나 판단처럼 원래 파일을 안 바꾸는 태스크는 `noCodeChange: true`를 같이 낸다. `artifacts`가 하나라도 있으면 `noCodeChange`와 상관없이 대조한다.
450
+
451
+ ```json
452
+ {
453
+ "action": "execute_task",
454
+ "sessionId": "...",
455
+ "taskResult": {
456
+ "taskId": "task-1",
457
+ "status": "completed",
458
+ "output": "기존 인증 흐름 조사 결과 요약",
459
+ "artifacts": [],
460
+ "noCodeChange": true
461
+ }
462
+ }
463
+ ```
464
+
465
+ 통과하면 응답에 `artifactCheck`가 붙는다.
466
+
467
+ - `verified`: 대조를 통과한 경우
468
+ - `no_code_change`: `noCodeChange: true`로 대조를 건너뛴 경우
469
+ - `no_baseline`: git 레포가 아니라 시작 시점 상태가 없는 경우
470
+ - `baseline_failed`: `execute_start` 때 git 호출이 실패해 시작 시점 상태를 못 잡은 경우
471
+ - `baseline_truncated`: 시작 시점에 커밋 안 된 파일이 2000개를 넘어 이벤트에 해시를 다 못 남겼고 이 세션을 다른 서버 프로세스가 이벤트에서 불러와 대조하지 못한 경우. 해시는 실행을 시작한 프로세스 메모리에만 있고 이벤트에는 사유만 남는다. 그래서 서버가 재시작됐거나 MCP 서버를 따로 띄우는 dispatch 워커가 보고하면 대조 없이 통과한다. 시작한 프로세스는 상태를 잡았으므로 `execute_start` 응답의 `artifactCheck.baseline`은 이때도 `captured`로 나간다. dispatch를 쓸 땐 시작 전에 커밋 안 된 파일을 줄여 둔다
472
+
473
+ **`verification_failed`를 받으면.** 바뀌지 않은 파일이 하나라도 있으면 서버는 결과를 기록하지 않고 이렇게 돌려준다.
474
+
475
+ ```json
476
+ {
477
+ "status": "verification_failed",
478
+ "recorded": false,
479
+ "problems": [{ "path": "src/auth.ts", "status": "unchanged", "hint": "실행 시작 뒤로 내용이 그대로입니다" }],
480
+ "nextAction": "execute_task"
481
+ }
482
+ ```
483
+
484
+ `problems[].status`는 `unchanged`, `missing`, `outside_repo`, `ignored`, `directory`, `invalid_path`(경로에 개행이나 NUL 문자), `not_regular_file`(FIFO나 소켓처럼 일반 파일이 아님) 중 하나다. 태스크는 아직 끝나지 않은 상태로 남는다. 파일을 실제로 고쳤는지 확인하고 수정을 마치거나 `artifacts` 목록을 바로잡아 같은 태스크를 다시 제출한다. 끝내지 못했으면 `status: "failed"`로 낸다.
485
+
486
+ 응답에 `serverError: true`가 있으면 보고 내용 문제가 아니라 서버 쪽 git 호출이 실패한 것이다. 이때는 고칠 것이 없으니 잠시 뒤 같은 결과를 그대로 다시 낸다. 계속 실패하면 `status: "failed"`로 내고 `output`에 오류 내용을 적는다.
487
+
488
+ > 기준점은 태스크마다가 아니라 `execute_start` 때 한 번 잡는다. 앞선 태스크가 바꾼 파일을 뒤 태스크가 `artifacts`에 적어도 통과한다.
489
+
386
490
  ---
387
491
 
388
492
  ## Phase 3 — Evaluate
@@ -390,12 +494,19 @@ role_match/role_consensus로 얻은 `roleGuidance`를 참조해 태스크를 수
390
494
  모든 태스크 완료 후 3-Call 평가 진행.
391
495
 
392
496
  **Call 1 — Structural 단계 시작**
497
+
393
498
  ```json
394
499
  { "action": "evaluate", "sessionId": "..." }
395
500
  ```
501
+
396
502
  → `{ stage: "structural", structuralContext }` — lint/build/test 실행 지시
397
503
 
504
+ `structuralContext.commands`의 `command` 문자열을 고치지 말고 그대로 실행한다. 패키지 매니저는 서버가 package.json의 `packageManager` 필드나 lockfile로 골라뒀다. scripts에 없는 lint, build는 목록에서 빠지고 test는 항상 남는다. 제출할 때도 같은 문자열을 그대로 싣는다 — 명령이 다르거나 빠지면 서버가 거부한다. 종료 코드가 0이 아닌 명령이 있으면 `allPassed`를 `true`로 보내도 실패로 처리된다.
505
+
506
+ 거부 메시지가 `Submitted commands do not match`로 시작하면 `structuralContext.commands`의 명령을 그대로 다시 실행해서 그 실행의 종료 코드와 출력으로 다시 제출한다. 이전 실행 결과에 명령 문자열만 바꿔 싣지 않는다. 다른 거부 메시지면 다시 제출하지 말고 Call 1부터 다시 시작한다.
507
+
398
508
  **Call 2 — Structural 결과 제출**
509
+
399
510
  ```json
400
511
  {
401
512
  "action": "evaluate",
@@ -410,24 +521,25 @@ role_match/role_consensus로 얻은 `roleGuidance`를 참조해 태스크를 수
410
521
  }
411
522
  }
412
523
  ```
413
- → structural 실패 시 `{ stage: "structural_failed", evolveContext }` → Evolve Flow A 진입
414
- → structural 통과 시 `{ stage: "contextual", evaluationContext }` — AC별 LLM 검증 지시
524
+
525
+ → structural 실패 시 `{ stage: "complete", shortCircuited: true, nextAction: "evolve_fix" }` → Evolve Flow A 진입
526
+ → structural 통과 시 `{ stage: "contextual", contextualContext }` — AC별 LLM 검증 지시
415
527
 
416
528
  **Call 3 — Contextual 결과 제출**
529
+
417
530
  ```json
418
531
  {
419
532
  "action": "evaluate",
420
533
  "sessionId": "...",
421
534
  "evaluationResult": {
422
- "verifications": [
423
- { "acIndex": 0, "satisfied": true, "evidence": "...", "gaps": [] }
424
- ],
535
+ "verifications": [{ "acIndex": 0, "satisfied": true, "evidence": "...", "gaps": [] }],
425
536
  "overallScore": 0.92,
426
537
  "goalAlignment": 0.88,
427
538
  "recommendations": []
428
539
  }
429
540
  }
430
541
  ```
542
+
431
543
  → `{ status: "completed" }` (score ≥ 0.85, goalAlignment ≥ 0.80)
432
544
  → 미달 시 `{ evolveContext }` → Evolve Flow B 진입
433
545
 
@@ -477,7 +589,7 @@ role_match/role_consensus로 얻은 `roleGuidance`를 참조해 태스크를 수
477
589
  {
478
590
  "action": "evolve_re_execute",
479
591
  "sessionId": "...",
480
- "reExecuteTaskResult": { "taskId": "task-3", "status": "completed", "output": "...", "artifacts": [] }
592
+ "reExecuteTaskResult": { "taskId": "task-3", "status": "completed", "output": "...", "artifacts": ["src/auth/oauth.ts"] }
481
593
  }
482
594
 
483
595
  // 4. Re-evaluate
@@ -510,25 +622,64 @@ role_match/role_consensus로 얻은 `roleGuidance`를 참조해 태스크를 수
510
622
  { "action": "evolve_lateral", "sessionId": "..." }
511
623
  ```
512
624
 
513
- | Stagnation 패턴 | Persona | 전략 |
625
+ | Stagnation 패턴 | Persona | 전략 |
626
+ | ------------------- | -------------- | ---------------- |
627
+ | hard_cap | Multistability | 다른 각도로 보기 |
628
+ | oscillation | Simplicity | 단순하게 줄이기 |
629
+ | no_drift | Reification | 빠진 조각 채우기 |
630
+ | diminishing_returns | Invariance | 성공 패턴 복제 |
631
+
632
+ 4개 persona를 다 써도 점수가 안 오르면 세션은 끝나지 않고 `awaiting_human`으로 멈춘다. 응답에 `gate`가 실려 온다.
633
+
634
+ ```json
635
+ → { status: "awaiting_human", gate: { gateId, question, options: [...], context }, escalationContext, nextAction: "gate_resolve", nextActionParams: { sessionId, gateResolution: { gateId } } }
636
+ ```
637
+
638
+ ### Flow D — 사람 판단 대기 (`awaiting_human`)
639
+
640
+ 여기서 스킬은 스스로 고르지 않는다. **반드시 사람에게 묻고 답을 기다린다.**
641
+
642
+ 1. `gate.question`과 `gate.context`(막힌 태스크, 시도한 persona, 최고 점수, 남은 문제)를 보여준다.
643
+ 2. `gate.options`를 선택지로 띄운다. 호스트에 선택형 질문 도구가 있으면(AskUserQuestion 등) 그걸 쓴다. 없으면 번호 목록으로 보여주고 답을 받는다. `restart`와 `abort`는 세션이 종료된다는 점을 함께 알린다.
644
+ 3. 고른 선택지에 더해 **결정(`decision`)과 이유(`rationale`)를 한 줄씩** 받는다. 둘 다 비면 서버가 거절한다. 사람이 이유를 안 적었으면 다시 묻는다. 대신 지어내지 않는다 — 이 두 줄이 그대로 Memory에 남는다.
645
+ 4. `gate_resolve`를 부른다.
646
+
647
+ ```json
648
+ {
649
+ "action": "gate_resolve",
650
+ "sessionId": "...",
651
+ "gateResolution": {
652
+ "gateId": "gate-...",
653
+ "optionId": "patch_spec",
654
+ "decision": "사람이 내린 결정",
655
+ "rationale": "그렇게 정한 이유"
656
+ }
657
+ }
658
+ ```
659
+
660
+ 5. 응답의 `nextAction`을 따른다.
661
+
662
+ | `optionId` | 응답 | 다음 |
514
663
  |---|---|---|
515
- | hard_cap | Multistability | 다른 각도로 보기 |
516
- | oscillation | Simplicity | 단순하게 줄이기 |
517
- | no_drift | Reification | 빠진 조각 채우기 |
518
- | diminishing_returns | Invariance | 성공 패턴 복제 |
664
+ | `patch_spec` | `gate_resolved`, 세션 `executing` | 사람이 말한 대로 AC나 제약을 고쳐 `evolve_patch` (Flow B) |
665
+ | `manual_task` | `gate_resolved`, 세션 `executing` | 사람이 태스크를 처리했는지 확인한 뒤 `evaluate` |
666
+ | `restart` | `terminated` | 세션 종료. 태스크를 더 잘게 쪼갠 새 스펙으로 `start`를 안내 |
667
+ | `abort` | `terminated` | 세션 종료. 막힌 지점과 시도한 접근을 보고 |
668
+
669
+ 이어간 뒤 다음 평가가 한 번만 성공 기준에 못 미쳐도 persona를 되풀이하지 않고 곧바로 새 `awaiting_human` 응답이 온다. 진화 이력과 이미 써본 persona 기록이 남아 있어서다. 그때도 1번부터 다시 한다.
519
670
 
520
- 4개 persona 소진 → `human_escalation` 반환으로 세션 종료.
671
+ `awaiting_human`인 동안에는 `status`, `resume`, `gate_resolve`, `evolution_viz` 말고는 전부 에러로 거절된다. 세션을 이어받았는데 `resume`이 `awaiting_human`을 돌려주면 1번부터 다시 한다.
521
672
 
522
673
  ### 종료 조건
523
674
 
524
- | 조건 | 트리거 |
525
- |------|--------|
526
- | `success` | score ≥ 0.85 AND goalAlignment ≥ 0.80 |
527
- | `stagnation` | 2회 연속 delta < 0.05 |
528
- | `oscillation` | 2회 연속 점수 역전 |
529
- | `hard_cap` | structural 3회 + contextual 3회 실패 |
530
- | `caller` | `{ action: "evolve", terminateReason: "caller" }` |
531
- | `human_escalation` | 4개 lateral persona 소진 |
675
+ | 조건 | 트리거 |
676
+ | ------------------ | ------------------------------------------------------------- |
677
+ | `success` | score ≥ 0.85 AND goalAlignment ≥ 0.80 |
678
+ | `stagnation` | 2회 연속 delta < 0.05 |
679
+ | `oscillation` | 2회 연속 점수 역전 |
680
+ | `hard_cap` | structural 3회 + contextual 3회 실패 |
681
+ | `caller` | `{ action: "evolve", terminateReason: "caller" }` |
682
+ | `human_escalation` | 4개 lateral persona 소진 후 사람이 `restart`나 `abort`를 고름 |
532
683
 
533
684
  ---
534
685
 
@@ -551,7 +702,13 @@ activeForm: "실행 계획 수립 중"
551
702
  `TaskUpdate`로 현재 Planning 단계를 업데이트한다.
552
703
 
553
704
  ```
554
- description: "Planning 중 | 단계 {stepsCompleted}/4 | {currentPrinciple}"
705
+ description: "Planning 중 | 단계 {stepsCompleted}/4 | {executeContext.currentStage}"
706
+ ```
707
+
708
+ `plan_rewound`를 받으면 `stepsCompleted`가 없으므로 이렇게 바꾼다.
709
+
710
+ ```
711
+ description: "Planning 되감김 | 단계 {rewoundToStep - 1}/4 | {executeContext.currentStage}"
555
712
  ```
556
713
 
557
714
  ### Execution 시작 후 (`execute_start` 응답 수신 후)
@@ -584,12 +741,14 @@ activeForm: "병렬 실행 중: 그룹 {groupIndex}/{totalGroups} — {agentCoun
584
741
  `TaskUpdate`로 진행 상황과 현재 실행 태스크명을 업데이트한다.
585
742
 
586
743
  순차 실행 중:
744
+
587
745
  ```
588
746
  description: "{completedCount}/{totalTasks} 완료 | 실패: {failedCount}개 | 그룹 {groupIndex}/{totalGroups}"
589
747
  activeForm: "실행 중: {taskContext.currentTask.title}"
590
748
  ```
591
749
 
592
750
  병렬 그룹 실행 중 (각 Agent의 execute_task 완료 시):
751
+
593
752
  ```
594
753
  description: "{completedCount}/{totalTasks} 완료 | 실패: {failedCount}개 | 그룹 {groupIndex}/{totalGroups}"
595
754
  activeForm: "병렬 실행 중: 그룹 {groupIndex}/{totalGroups} — {agentCount}개 Agent 실행 중"
@@ -609,6 +768,14 @@ activeForm: "병렬 실행 중: 그룹 {groupIndex}/{totalGroups} — {agentCoun
609
768
  - 평가 완료(success): `TaskUpdate({ status: "completed", description: "완료 | score: {overallScore} | alignment: {goalAlignment}" })`
610
769
  - Evolve 진입: description에 "개선 중 (generation {N})" 추가
611
770
 
771
+ ### 사람 판단 대기 시 (`awaiting_human`)
772
+
773
+ ```
774
+ description: "사람 판단 대기 | 최고 점수: {gate.context.bestScore}"
775
+ ```
776
+
777
+ `gate_resolve`가 `gate_resolved`를 돌려주면 진화 단계 표시로 돌아간다.
778
+
612
779
  ### 오류/에스컬레이션 시
613
780
 
614
781
  ```
@@ -128,7 +128,15 @@ The interview only has value if the human's actual intent is captured. A self-an
128
128
  1. **Start**: Create a session, detect project type (greenfield/brownfield), ask the first question
129
129
  2. **Iterate**: Present each generated question to the user → wait for answer → submit answer to Gestalt
130
130
  3. **Score**: Continuously assess resolution across multiple dimensions
131
- 4. **Complete**: When resolution score ≥ 0.8, the interview is ready for spec generation
131
+ 4. **Complete**: Call `complete` only after `resolutionScore.isReady === true` (overall ≥ 0.8). The server rejects `complete` on a session that has no score or is below threshold
132
+
133
+ ### 0.8 미만에서 끝내기
134
+
135
+ 사용자가 0.8 미만에서 넘어가겠다고 직접 답한 경우에만 `force: true`로 `complete`를 부른다. 15라운드를 다 채워 `respond`가 더 받지 않으면 그 마지막 답의 점수는 반영되지 않은 상태다. 먼저 `score`로 그 점수를 넣고 `isReady`를 확인한다. 그래도 0.8 미만이면 에이전트가 현재 점수와 덜 채워진 차원을 보여주고 넘어갈지 묻는다. 라운드 수만으로 `force`를 넘기지 않는다. 사용자가 먼저 "그냥 넘어가자"고 했을 때도 같은 내용을 보여주고 한 번 더 확인한 뒤에 부른다.
136
+
137
+ 사용자가 넘어가지 않겠다고 하면 세션을 `in_progress`로 둔다. 15라운드 전이면 같은 세션에서 덜 채워진 차원을 다시 묻는다. 15라운드를 다 채웠으면 덜 채워진 차원을 사용자와 함께 정리해 새 인터뷰로 이어가거나 거기서 멈춘다.
138
+
139
+ 에이전트가 스스로 `force`를 판단하지 않는다. 거절 메시지에 "force=true로 넘기세요"가 나와도 그건 사용자에게 물어볼 근거이지 바로 넘길 근거가 아니다. 이렇게 끝낸 세션에는 `forcedComplete`가 기록된다. Spec을 뽑을 때도 `ges_generate_spec`에 `force`를 따로 넘겨야 한다.
132
140
 
133
141
  ## Gestalt Principles Applied
134
142
 
@@ -154,9 +162,12 @@ API 키 없이 MCP 서버 실행 시 자동 활성화. LLM 작업을 caller가
154
162
  | `resolutionScore.priorityClarity` | number (0-1) | 필수* | 우선순위 명확도 |
155
163
  | `resolutionScore.contextClarity` | number (0-1) | 선택 | 컨텍스트 명확도 |
156
164
  | `resolutionScore.contradictions` | string[] | 선택 | 발견된 모순 목록 |
165
+ | `force` | boolean | 선택 (기본 `false`) | `complete` 전용. 0.8 미만이어도 완료한다. 사용자가 직접 정했을 때만 넘긴다 |
157
166
 
158
167
  \* resolutionScore 객체를 제공할 경우 필수
159
168
 
169
+ 답변 라운드가 0인 세션에 기준을 넘는 resolutionScore를 넣으면 `score`와 `respond`가 거절한다. 공백만 보낸 답은 답변으로 세지 않는다. 기준 미만 점수는 받는다.
170
+
160
171
  ### Action별 응답 구조 (Passthrough)
161
172
 
162
173
  **`start`** → `{ status, sessionId, projectType, detectedFiles, gestaltContext, roundNumber, message }`
@@ -166,7 +177,9 @@ API 키 없이 MCP 서버 실행 시 자동 활성화. LLM 작업을 caller가
166
177
  **`score`** (점수 미제공 시) → `{ status, resolutionScore, scoringPrompt, message }`
167
178
  **`score`** (점수 제공 시) → `{ status, resolutionScore }`
168
179
 
169
- **`complete`** → `{ status, sessionId, totalRounds, finalResolutionScore, message }`
180
+ **`complete`** → `{ status, sessionId, totalRounds, finalResolutionScore, forced, message }`
181
+
182
+ `forced`는 기준 미만 세션을 `force`로 끝냈을 때만 `true`다. 이미 준비된 세션에 `force`를 넘겨도 `false`로 남는다.
170
183
 
171
184
  ### GestaltContext 필드
172
185
 
@@ -1303,6 +1303,22 @@ ges_execute {
1303
1303
 
1304
1304
  **그 67KB를 통째로 딸려 보내지도 않습니다.** 윤문에 넘기기 전에 먼저 스캔을 돌려 이번 리포트에서 실제로 걸린 룰만 처방과 함께 프롬프트에 싣습니다. 걸린 게 서너 개인데 예순 개가 넘는 룰북을 라운드마다 펼치면 모델이 나눠 쓰는 주의가 안 걸린 룰까지 흩어집니다. `ship`이 이 파이프라인을 라운드로 돌리는 자리라 그 낭비가 라운드마다 되풀이됩니다.
1305
1305
 
1306
+ #### 4.7단계 코멘트 작성과 함께 띄우기
1307
+
1308
+ **`prTarget`이 `github`이나 `local`이면 이 윤문과 4.7단계 코멘트 본문 작성을 한 메시지에 담아 병렬로 돌립니다.** 두 호출은 서로의 산출물을 받지 않습니다. `humanize-monolith`는 `review_consensus`가 돌려준 리포트와 사전 스캔 출력만 받습니다. `code-review-writer`는 `mergedIssues`와 `audience`만 받습니다. 윤문한 리포트는 코멘트의 입력이 아니고 코멘트도 리포트의 입력이 아니라서 차례로 기다릴 이유가 없습니다. consensus 뒤 구간이 이 스킬에서 제일 오래 걸리는 자리이고(2026-09-24 이후 게시까지 간 리뷰의 중앙값이 10분이었습니다) 그 안에서 에이전트 하나를 통째로 기다리던 시간이 여기서 빠집니다. 이 프롬프트에 코멘트 본문을 싣거나 코멘트 프롬프트에 윤문한 리포트를 싣는 변경은 이 전제와 부딪힙니다.
1309
+
1310
+ 띄우기 전에 메인이 끝내 두는 일이 있습니다. 전부 셸 호출 한두 번이라 에이전트를 기다리는 시간에 비하면 짧습니다.
1311
+
1312
+ 1. 아래 `사전 스캔`을 돌립니다. 그 출력이 윤문 프롬프트에 들어갑니다.
1313
+ 2. 4.7단계 `consensus 일치 검사`를 합니다. stale이면 둘 다 띄우지 않고 그 절이 정한 대로 1단계부터 다시 돌립니다. 낡은 consensus로 리포트와 코멘트를 둘 다 만들고 나서 버리게 됩니다.
1314
+ 3. 4.7단계 `게시 준비`의 PR 확인과 `audience` 확인을 합니다.
1315
+
1316
+ **게시 확인은 두 결과가 다 돌아온 뒤에 받습니다.** 4.7단계 `게시 확인` 절의 자리입니다. 사용자가 게시를 거절하면 `code-review-writer` 결과는 버립니다. 거절하는 일은 드뭅니다. 그 낭비보다 사용자가 확인 뒤에 에이전트를 한 번 더 기다리지 않게 하는 쪽이 낫습니다.
1317
+
1318
+ **두 어투 검사도 한 메시지에서 함께 돌립니다.** 아래 `리포트 어투 검사`와 4.7단계 `어투 검사`는 register가 다르고 파일도 따로 씁니다. 합쳐서 한 번에 부르지 않습니다(각 절에 근거가 있습니다). 한쪽만 걸리면 그쪽 에이전트만 다시 부릅니다.
1319
+
1320
+ `prTarget`이 `none`이면 윤문만 띄웁니다. 대화 도중 게시만 따로 요청받은 경로는 이 단계를 안 거치므로 4.7단계를 적힌 순서대로 따릅니다.
1321
+
1306
1322
  #### 사전 스캔 (필수)
1307
1323
 
1308
1324
  `review_consensus`가 돌려준 리포트를 윤문에 넘기기 전에 먼저 스캔합니다. 이 스캔의 출력이 아래 윤문 프롬프트의 입력이 됩니다.
@@ -1329,6 +1345,8 @@ gestalt humanize-scan --file "$scanTmp/consensus.md" --register report
1329
1345
 
1330
1346
  #### 윤문 위임
1331
1347
 
1348
+ `prTarget`이 `github`이나 `local`이면 이 호출을 4.7단계 `코멘트 본문 작성`의 호출과 같은 메시지에 담습니다.
1349
+
1332
1350
  ```
1333
1351
  Agent {
1334
1352
  subagent_type: "Explore",
@@ -1391,7 +1409,7 @@ gestalt humanize-scan --file "$scanTmp/report.md" --register report
1391
1409
 
1392
1410
  `--register report`입니다. 리포트는 서술체 문서라 룰북이 `report`에서 함께 보는 평서체와 합니다체의 혼용 검사까지 봐야 합니다. 코드펜스 안쪽은 register와 무관하게 항상 스캔에서 빠집니다. **`>` 블록인용 제외는 `chat`에서만 켜지므로 여기서는 함께 스캔됩니다** — 리포트가 이슈 원문이나 PR 본문을 인용하는 자리에서 그 원문이 걸릴 수 있습니다. 걸리면 인용 표시는 유지한 채로 `humanize-monolith`가 그 문장만 다시 씁니다.
1393
1411
 
1394
- 윤문된 리포트를 사용자에게 표시합니다. 그다음 `prTarget`이 `github`이나 `local`이면 4.7단계로 넘어갑니다. `none`이면 결과 표시로 넘어갑니다.
1412
+ 윤문된 리포트를 사용자에게 표시합니다. 그다음 `prTarget`이 `github`이나 `local`이면 4.7단계 `게시 확인`으로 넘어갑니다. 함께 띄운 코멘트 작성과 그 어투 검사도 여기까지 끝내 둡니다. `none`이면 결과 표시로 넘어갑니다.
1395
1413
  - `approved: true` → 리뷰 통과. 리포트를 보여줍니다.
1396
1414
  - `approved: false` → critical/high 이슈가 남아 Block 상태입니다.
1397
1415
 
@@ -1407,7 +1425,7 @@ gestalt humanize-scan --file "$scanTmp/report.md" --register report
1407
1425
 
1408
1426
  #### consensus 일치 검사 (stale이면 게시 금지)
1409
1427
 
1410
- 게시 직전에, 게시하려는 consensus가 **현재 diff와 일치하는지** 반드시 확인합니다. 리뷰를 끝낸 뒤 코드가 바뀌었거나(커밋 추가·로컬 수정), 애초에 활성 리뷰 세션이 없으면 그 consensus는 stale이므로 **그대로 올리지 않습니다.**
1428
+ 코멘트를 쓰기 전에, 게시하려는 consensus가 **현재 diff와 일치하는지** 반드시 확인합니다. 4.5단계에서 윤문과 함께 띄우는 흐름이면 두 에이전트를 띄우기 전에 합니다. 리뷰를 끝낸 뒤 코드가 바뀌었거나(커밋을 더했거나 로컬에서 고쳤거나), 애초에 활성 리뷰 세션이 없으면 그 consensus는 stale이므로 **그대로 올리지 않습니다.**
1411
1429
 
1412
1430
  ```bash
1413
1431
  # 리뷰 시점 대비 작업트리가 바뀌었는지 확인 — 대상과 무관하게 공통
@@ -1427,7 +1445,7 @@ gestalt pr --json show <id> # headSha 필드로 비교
1427
1445
 
1428
1446
  인라인 코멘트는 **언제 요청받든 항상 "현재 diff 기준 consensus + code-review-writer voice"** 로만 게시됩니다. 옛 리뷰 메모리를 그대로 옮겨 적거나 Claude가 손으로 코멘트를 짜는 경로는 없습니다.
1429
1447
 
1430
- #### 게시 준비 (PR 식별, audience 확인, 게시 확인)
1448
+ #### 게시 준비 (PR 식별, audience 확인)
1431
1449
 
1432
1450
  **PR 식별.** 대상 판별은 1단계 직후에 이미 끝났습니다. 여기서는 그때 보관한 `prTarget`과 PR 식별자를 그대로 씁니다. **같은 조회를 다시 하지 않습니다.**
1433
1451
 
@@ -1445,10 +1463,10 @@ gestalt pr --json show <id> 2>/dev/null
1445
1463
 
1446
1464
  **`audience` 확인이 남았는지 봅니다.** 1.05단계를 안 거치고 이 단계로 바로 들어온 경로(대화 도중 "이제 PR에 코멘트 남겨줘")가 있습니다. `audience`가 `junior`인데 `prTarget`이 `local`이고 그 확인을 받은 기록이 없으면 여기서 먼저 묻습니다 — 1.05단계와 같은 문구입니다.
1447
1465
 
1448
- **게시 확인.** PR이 식별되면 사용자에게 한 번 확인합니다: **"발견된 이슈 N개를 PR #<number 또는 로컬 PR id>에 인라인 코멘트로 게시할까요?"** N은 `mergedIssues` 수입니다. 3.7단계에서 뺀 이슈는 세지 않습니다. 동의하지 않으면 리포트만 보여주고 종료합니다.
1449
-
1450
1466
  #### 코멘트 본문 작성 (code-review-writer)
1451
1467
 
1468
+ 4.5단계를 거쳐 온 흐름이면 이 호출은 4.5단계 `윤문 위임`의 호출과 같은 메시지에 담깁니다. 게시만 따로 요청받은 경로에서는 혼자 띄웁니다.
1469
+
1452
1470
  **서브에이전트에 위임합니다.** 이 에이전트는 본문 18.8KB에 `author-voice.md` 23KB를 딸고 오는, 이 스킬에서 제일 무거운 자리입니다.
1453
1471
 
1454
1472
  **`author-voice.md`는 읽고 `ai-tell-quick-rules.md`는 첫 판에 안 엽니다.** 앞의 것은 코멘트를 쓰는 기준이라 없으면 쓰기 자체가 안 됩니다. 뒤의 것은 다 쓴 뒤에 무엇이 걸렸는지 보는 기준입니다. 그 답은 아래 `어투 검사`가 걸린 룰의 처방까지 붙여 돌려줍니다. 44KB를 미리 펼쳐 놓고 그중 서너 줄을 쓰는 대신, 걸린 자리의 처방만 재작성 판에 실어 보냅니다.
@@ -1568,6 +1586,12 @@ find "$scanTmp" -maxdepth 1 -name '*.md' | sort | sed 's/^/--file /' \
1568
1586
 
1569
1587
  **큰따옴표로 감싼 평문은 형태로 못 가릅니다.** 한국어에서 큰따옴표는 인용만이 아니라 강조로도 쓰여서요. 룰북 Do-NOT 목록이 "큰따옴표 안 직접 인용"을 예외로 두지만 그건 사람과 모델이 판단하는 자리의 기준입니다. 자동 스캔은 위 셋만 뺍니다. 원문을 가리켜야 하면 백틱이나 블록인용을 씁니다.
1570
1588
 
1589
+ #### 게시 확인
1590
+
1591
+ 코멘트 본문과 어투 검사가 끝나면 게시하기 전에 사용자에게 한 번 확인합니다: **"발견된 이슈 N개를 PR #<number 또는 로컬 PR id>에 인라인 코멘트로 게시할까요?"** N은 `mergedIssues` 수입니다. 3.7단계에서 뺀 이슈는 세지 않습니다. 동의하지 않으면 써 둔 코멘트는 버리고 리포트만 보여준 채 종료합니다.
1592
+
1593
+ 코멘트를 다 쓴 뒤에 묻는 건 사용자가 확인한 다음 에이전트를 한 번 더 기다리지 않게 하려는 것입니다. 4.5단계와 함께 띄우는 흐름에서는 확인을 먼저 받으면 병렬로 돌릴 수가 없습니다. 게시만 따로 요청받은 경로도 같은 자리에서 묻습니다.
1594
+
1571
1595
  #### 리뷰 이벤트 결정 (postVerdict와 본인 PR 예외)
1572
1596
 
1573
1597
  `mergedIssues`의 `severity`로 리뷰 전체의 `event`를 정합니다. 본문 첫 글자를 파싱하지 않습니다 — 접두어는 사람이 읽는 라벨이지 판정 입력이 아닙니다.