fix(edge): review 검증 진입을 강제한다

This commit is contained in:
toki 2026-08-15 10:08:00 +09:00
parent 0feb3b5e83
commit 5dfce5fd90
5 changed files with 28 additions and 3 deletions

View file

@ -1722,7 +1722,7 @@ func hotPathStageRunInput(snapshot hotPathDispatchSnapshot, prompt string) map[s
}
if tools := hotPathChatTools(hotPathStageProviderTools(snapshot)); len(tools) > 0 {
input["tools"] = tools
input["tool_choice"] = "auto"
input["tool_choice"] = hotPathChatStageToolChoice(snapshot)
}
options := cloneAnyMap(snapshot.Stage.Options)
if options == nil {
@ -1743,7 +1743,7 @@ func hotPathChatStageBody(snapshot hotPathDispatchSnapshot, prompt, target strin
}
if tools := hotPathChatTools(hotPathStageProviderTools(snapshot)); len(tools) > 0 {
body["tools"] = tools
body["tool_choice"] = "auto"
body["tool_choice"] = hotPathChatStageToolChoice(snapshot)
}
reserved := map[string]struct{}{"model": {}, "messages": {}, "tools": {}, "stream": {}}
if snapshot.OutputBudget.Limited {
@ -1763,7 +1763,7 @@ func hotPathAnthropicStageBody(snapshot hotPathDispatchSnapshot, prompt, target
}
if tools := hotPathAnthropicTools(hotPathStageProviderTools(snapshot)); len(tools) > 0 {
body["tools"] = tools
body["tool_choice"] = map[string]any{"type": "auto"}
body["tool_choice"] = map[string]any{"type": hotPathChatStageToolChoice(snapshot)}
}
reserved := map[string]struct{}{"model": {}, "messages": {}, "tools": {}, "stream": {}}
if snapshot.OutputBudget.Limited {
@ -1774,6 +1774,13 @@ func hotPathAnthropicStageBody(snapshot hotPathDispatchSnapshot, prompt, target
return json.Marshal(body)
}
func hotPathChatStageToolChoice(snapshot hotPathDispatchSnapshot) string {
if snapshot.Phase == hotPathPhaseReviewActive && len(snapshot.Transcript) == 0 {
return "required"
}
return "auto"
}
func hotPathStageProviderTools(snapshot hotPathDispatchSnapshot) []any {
return cloneAnySlice(snapshot.Tools)
}

View file

@ -103,6 +103,21 @@ func TestWorkerProviderReceivesOnlyCallerTools(t *testing.T) {
}
}
func TestInitialReviewRequiresToolUseBeforeTerminal(t *testing.T) {
initial := hotPathDispatchSnapshot{Phase: hotPathPhaseReviewActive}
if got := hotPathChatStageToolChoice(initial); got != "required" {
t.Fatalf("initial review tool choice = %q, want required", got)
}
retry := initial
retry.Transcript = []hotPathStageExchange{{}}
if got := hotPathChatStageToolChoice(retry); got != "auto" {
t.Fatalf("continued review tool choice = %q, want auto", got)
}
if got := hotPathChatStageToolChoice(hotPathDispatchSnapshot{Phase: hotPathPhaseLocalActive}); got != "auto" {
t.Fatalf("worker tool choice = %q, want auto", got)
}
}
func TestHotPathLightRequiresPlanReadBeforeWorkerCompletion(t *testing.T) {
for _, endpoint := range []string{"openai", "anthropic"} {
endpoint := endpoint

View file

@ -11,6 +11,7 @@ import (
const hotPathReviewSystemPrompt = `You are the Reviewer in a compact Plan -> Work -> Review -> Repair pipeline.
Read the exact issued Plan and worker-filled Review before judging the task. Treat Review as worker evidence, not proof.
Inspect the actual caller-workspace result and rerun the Plan's applicable verification with ordinary caller tools.
Your first response must issue tool calls that read both exact artifact paths and inspect the actual caller-workspace result; do not return terminal prose first.
Check correctness, completeness, requirement coverage, verification trust, and unjustified deviations.
If a defect exists, establish its evidence and root cause, choose one concrete fix, repair it with ordinary caller tools, and reverify in this same Review stage.
Do not rewrite the reserved Plan or Review artifacts. Do not create a separate Result or final-review document.

View file

@ -40,6 +40,7 @@ REVIEW path: %s
You are the Planner. Apply this compact Plan workflow before authoring the pair:
1. Analyze the immutable user task first. Do not create a separate analysis artifact.
2. Preserve every explicit requirement, constraint, deliverable, and acceptance condition. Do not invent extra scope.
Copy every exact literal, filename, command, and required output string from the task verbatim into an executable Plan step or Verification bullet.
3. Convert that analysis into 2-6 closed, executable steps. The Worker must be able to implement without rediscovering requirements or choosing among alternatives.
4. Write 1-3 deterministic verification bullets with observable pass conditions.
5. Make the final Plan step require the Worker to read the pending REVIEW path above, then as the Work stage's final action replace that same file while preserving its exact headings. Every Plan item status must be exactly "completed"; actual changes, actual verification evidence, and deviations must be non-empty.

View file

@ -279,6 +279,7 @@ func TestHotPathSelectorPairInstructionCarriesCompactPlanContract(t *testing.T)
"You are the Planner.",
"Analyze the immutable user task first.",
"Preserve every explicit requirement, constraint, deliverable, and acceptance condition.",
"Copy every exact literal, filename, command, and required output string",
"The Worker must be able to implement without rediscovering requirements",
"Make the final Plan step require the Worker to read the pending REVIEW path",
"Every Plan item status must be exactly \"completed\"",