Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
runs-on: ubuntu-latest
env:
# Reproduce go/Dockerfile's sparse SDK clone; keep in sync with its AGENTFIELD_SDK_REF.
AGENTFIELD_SDK_REF: 054a7d18b4bfbd6b48f8582a337894c3c5975d36
AGENTFIELD_SDK_REF: 20955b2637b4708758c328a4f64fe460c7d4b772
AGENTFIELD_REPO: https://github.com/Agent-Field/agentfield.git
GOWORK: off
steps:
Expand Down
2 changes: 1 addition & 1 deletion go/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ FROM golang:1.23-bookworm AS builder

# Pinned AgentField SDK ref. Default = agentfield origin/main HEAD at port time
# (v0.1.107-rc.1). Changing this string invalidates the clone layer below.
ARG AGENTFIELD_SDK_REF=054a7d18b4bfbd6b48f8582a337894c3c5975d36
ARG AGENTFIELD_SDK_REF=20955b2637b4708758c328a4f64fe460c7d4b772
ARG AGENTFIELD_REPO=https://github.com/Agent-Field/agentfield.git

WORKDIR /src
Expand Down
2 changes: 1 addition & 1 deletion go/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module github.com/Agent-Field/SWE-AF/go
go 1.21

require (
github.com/Agent-Field/agentfield/sdk/go v0.0.0-20260721154150-054a7d18b4bf
github.com/Agent-Field/agentfield/sdk/go v0.0.0-20260723130821-20955b2637b4
github.com/invopop/jsonschema v0.13.0
golang.org/x/sync v0.11.0
)
Expand Down
4 changes: 2 additions & 2 deletions go/go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
github.com/Agent-Field/agentfield/sdk/go v0.0.0-20260721154150-054a7d18b4bf h1:wTYUlaz81NirvBpFC7tdXeCpHCWcmaKQZFAqamNZTJw=
github.com/Agent-Field/agentfield/sdk/go v0.0.0-20260721154150-054a7d18b4bf/go.mod h1:08VZk14uw4GJH6a34psHkuLu+DcRr197Zi0IGmLlfrM=
github.com/Agent-Field/agentfield/sdk/go v0.0.0-20260723130821-20955b2637b4 h1:OwOEyxRfYD0n2LAmaJIJdfejWpIYgRAf9oq/YA4qfVk=
github.com/Agent-Field/agentfield/sdk/go v0.0.0-20260723130821-20955b2637b4/go.mod h1:08VZk14uw4GJH6a34psHkuLu+DcRr197Zi0IGmLlfrM=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
Expand Down
4 changes: 4 additions & 0 deletions go/internal/roles/coding/coding.go
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,10 @@ func parseSynthesis(resp *ai.Response) (*schemas.QASynthesisResult, bool) {
if err := resp.JSON(&out); err != nil {
return nil, false
}
// The system prompt names the actions in uppercase (FIX/APPROVE/BLOCK) and
// the reflected request schema carries no enum, so models routinely answer
// in uppercase. Normalize before the enum check.
out.Action = schemas.QASynthesisAction(strings.ToLower(strings.TrimSpace(string(out.Action))))
switch out.Action {
case schemas.QASynthesisActionFix, schemas.QASynthesisActionApprove, schemas.QASynthesisActionBlock:
return &out, true
Expand Down
30 changes: 30 additions & 0 deletions go/internal/roles/coding/coding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -593,3 +593,33 @@ func TestInputDefaults(t *testing.T) {
t.Fatalf("qa_synthesizer default model must be haiku, got %q", si.Model)
}
}

// Contract: models routinely answer the action in uppercase (the system prompt
// names FIX/APPROVE/BLOCK and the reflected request schema carries no enum), so
// parseSynthesis must case-normalize instead of dropping the synthesis and
// triggering the deterministic fallback.
func TestRunQASynthesizerNormalizesUppercaseAction(t *testing.T) {
nr := &noteRecorder{}
mai := &mockAI{resp: aiJSONResponse(`{"action":"APPROVE","summary":"env failures are pre-existing","stuck":false}`)}
mh := &mockHarness{fn: func(_ any) (*harness.Result, error) {
t.Fatal("run_qa_synthesizer must not call the harness")
return nil, nil
}}

out, err := RunQASynthesizer(context.Background(), newDeps(mh, mai, nr), map[string]any{
"qa_result": map[string]any{"passed": false},
"review_result": map[string]any{"approved": true},
"iteration_history": []any{},
"iteration_id": "s2",
})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
m := asMap(t, out)
if m["action"] != "approve" {
t.Fatalf("expected normalized action \"approve\", got %v", m["action"])
}
if m["summary"] != "env failures are pre-existing" {
t.Fatalf("model synthesis was discarded for the fallback: %v", m)
}
}
Loading