From 48b69740df9e66bd363526ae8387048c25ce3259 Mon Sep 17 00:00:00 2001 From: toki Date: Sat, 15 Aug 2026 10:51:38 +0900 Subject: [PATCH] =?UTF-8?q?fix(edge):=20Gemini=20selector=20=EC=84=9C?= =?UTF-8?q?=EB=AA=85=20=EA=B2=BD=EA=B3=84=EB=A5=BC=20=EB=B6=84=EB=A6=AC?= =?UTF-8?q?=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/edge/internal/openai/artifact_pair.go | 10 ++++++++++ apps/edge/internal/openai/artifact_pair_test.go | 17 +++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/apps/edge/internal/openai/artifact_pair.go b/apps/edge/internal/openai/artifact_pair.go index 18df606a..0f5a8c4e 100644 --- a/apps/edge/internal/openai/artifact_pair.go +++ b/apps/edge/internal/openai/artifact_pair.go @@ -246,6 +246,16 @@ func (s *artifactFrontierStore) expandSelectorPair( if providerID == "" { providerID = strings.TrimSpace(call.ID) } + // The selector stage ends after its atomic pair is expanded into caller + // workspace writes, so its Gemini thought signature is never replayed to + // the selector provider. Keep only the provider's original tool id before + // deriving the two internal write ids; the opaque signature envelope can be + // larger than the logical-request id contract permits. + if decodedID, _, encoded, err := decodeGeminiThoughtSignatureToolID(providerID); err != nil { + return normalizedStageOutput{}, err + } else if encoded { + providerID = decodedID + } if !validLogicalRequestID(providerID) { return normalizedStageOutput{}, fmt.Errorf("artifact pair provider tool id is invalid") } diff --git a/apps/edge/internal/openai/artifact_pair_test.go b/apps/edge/internal/openai/artifact_pair_test.go index 82257762..a55503aa 100644 --- a/apps/edge/internal/openai/artifact_pair_test.go +++ b/apps/edge/internal/openai/artifact_pair_test.go @@ -364,6 +364,23 @@ func TestArtifactSelectorAtomicPairExpandsToCallerWrites(t *testing.T) { } } +func TestArtifactSelectorAtomicPairDropsGeminiThoughtSignatureEnvelope(t *testing.T) { + fixture := newArtifactPairFixture(t, "openai", true) + providerID := encodeGeminiThoughtSignatureToolID("provider_pair", strings.Repeat("signature", 64)) + output, err := fixture.server.artifactFrontiers.expandSelectorPair(fixture.requestID, fixture.ownerEdgeID, normalizedStageOutput{ + ToolCalls: []normalizedToolCall{{ + ID: providerID, ProviderCallID: providerID, Name: hotPathArtifactPairToolName, + Arguments: map[string]any{"plan_content": testLightweightPlan(t), "review_content": testPendingReview(t)}, + }}, + }) + if err != nil { + t.Fatal(err) + } + if len(output.ToolCalls) != 2 || output.ToolCalls[0].ProviderCallID != "provider_pair_plan" || output.ToolCalls[1].ProviderCallID != "provider_pair_review" { + t.Fatalf("expanded provider ids=%+v", output.ToolCalls) + } +} + func (f *artifactPairFixture) issuePrepare() []string { f.t.Helper() ids, err := f.issue([]normalizedToolCall{{