fix(delivery): align desktop marker test with exact-cut rule; trusted task text for parallel_tasks

Second review round:

- The desktop topic-title regression test built its message from a
  paraphrased delivery-runtime marker, which the byte-exact suffix cut
  correctly refuses to strip — the desktop CI job failed on it. The marker
  constant is now exported (agent.DeliveryRuntimeMarker) as the single
  source of truth; the test builds from it, and the exact-match contract
  stays intact. Its text is byte-identical since introduction, so the exact
  cut is safe for all persisted sessions.
- parallel_tasks children now pass the pristine task through
  Options.ClassifierTaskText before workspace wrapping, matching
  runSubSession. Without it the wrapped text hit the IsTask length fallback
  and a conversational child exhausted final-answer readiness instead of
  answering; regression test added (fails on the previous code).
This commit is contained in:
SivanCola committed 2026-07-13 03:45:18 +08:00
1 parent e3052d3ce3
commit f3029e35ed
6 files changed
+51 -10

No files matched your search

+3 -1
View File
@@ -389,7 +389,9 @@ func TestTopicTitleUserTurnsSkipHostFraming(t *testing.T) {
path := filepath.Join(dir, "session.jsonl")
s := agent.NewSession("sys")
// Delivery-mode first turn: user text with the trailing runtime marker.
s.Add(provider.Message{Role: provider.RoleUser, Content: "你是谁?\n\n<delivery-runtime>\nThis session is in delivery-first mode.\n</delivery-runtime>"})
// Built from the exported constant — the preview strip is byte-exact, so a
// paraphrased marker would (correctly) not be stripped.
s.Add(provider.Message{Role: provider.RoleUser, Content: "你是谁?\n\n" + agent.DeliveryRuntimeMarker})
s.Add(provider.Message{Role: provider.RoleAssistant, Content: "reply"})
// Host-injected readiness nudge, persisted as role user.
s.Add(provider.Message{Role: provider.RoleUser, Content: "Host final-answer readiness check failed. Before giving a final answer, address the missing host-observable receipts: x"})
+8 -3
View File
@@ -50,7 +50,12 @@ const maxExecutorHandoffNudges = 1
const memoryCompilerInjectionMax = 5
const memoryCompilerInjectionCooldown = 30 * time.Second
const deliveryRuntimeMarker = `<delivery-runtime>
// DeliveryRuntimeMarker is the delivery-mode contract block appended to user
// turns (withTurnPreferences). Exported as the single source of truth for the
// byte-exact suffix strip in preview derivation and for cross-package tests;
// its text is cache-frozen — changing it breaks steer replay matching and the
// prefix stability of every live delivery session.
const DeliveryRuntimeMarker = `<delivery-runtime>
This session is in delivery-first mode. Before any state-changing tool call,
establish concrete, verifiable acceptance criteria with todo_write. After the
change, inspect the result, run relevant verification, and sign off each step
@@ -758,7 +763,7 @@ func (a *Agent) withTurnPreferences(input string) string {
}
input = WithReasoningLanguage(input, lang)
if a.deliveryProfile && !strings.Contains(input, "<delivery-runtime>") {
input = strings.TrimSpace(input) + "\n\n" + deliveryRuntimeMarker
input = strings.TrimSpace(input) + "\n\n" + DeliveryRuntimeMarker
}
return input
}
@@ -856,7 +861,7 @@ func SteerText(content string) (string, bool) {
if after, found := strings.CutPrefix(s, MidTurnSteerPrefix); found {
// Strip only the "\n" separator, preserving the user's original text.
after = strings.TrimPrefix(after, "\n")
if trimmed, cut := strings.CutSuffix(after, "\n\n"+deliveryRuntimeMarker); cut {
if trimmed, cut := strings.CutSuffix(after, "\n\n"+DeliveryRuntimeMarker); cut {
after = trimmed
}
return after, true
+3 -3
View File
@@ -226,7 +226,7 @@ func TestFinalReadinessBudgetExtendsOnlyWithProgress(t *testing.T) {
}
func TestPreviewStripsDeliveryMarkerAndSyntheticTurns(t *testing.T) {
first := "你是谁?\n\n" + deliveryRuntimeMarker
first := "你是谁?\n\n" + DeliveryRuntimeMarker
if got := UserPreviewText(first); got != "你是谁?" {
t.Fatalf("UserPreviewText kept framing: %q", got)
}
@@ -241,9 +241,9 @@ func TestPreviewStripsDeliveryMarkerAndSyntheticTurns(t *testing.T) {
{Role: provider.RoleSystem, Content: "sys"},
{Role: provider.RoleUser, Content: first},
{Role: provider.RoleAssistant, Content: "hi"},
{Role: provider.RoleUser, Content: finalReadinessRetryMessage("missing receipts") + "\n\n" + deliveryRuntimeMarker},
{Role: provider.RoleUser, Content: finalReadinessRetryMessage("missing receipts") + "\n\n" + DeliveryRuntimeMarker},
{Role: provider.RoleUser, Content: MidTurnSteerPrefix + "\nslow down"},
{Role: provider.RoleUser, Content: "帮我写一个魂斗罗游戏\n\n" + deliveryRuntimeMarker},
{Role: provider.RoleUser, Content: "帮我写一个魂斗罗游戏\n\n" + DeliveryRuntimeMarker},
}
preview, turns := SessionPreviewFromMessages(msgs)
if preview != "你是谁?" {
+6 -1
View File
@@ -180,8 +180,13 @@ func (p *ParallelTasksTool) Execute(ctx context.Context, args json.RawMessage) (
}
sess := NewSession(DefaultReadOnlyTaskSystemPrompt)
opts := p.taskTool.subagentOptions(ctx, max, pricing, ctxWin, childDepth)
// Same contract as runSubSession: capture the pristine task before
// host framing is prepended so delivery intent classification judges
// the task, not the wrapper.
opts.ClassifierTaskText = t.Prompt
output, runErr := RunSubAgentWithSession(ctx, prov, subReg, sess, p.taskTool.withWorkspaceContext(t.Prompt),
p.taskTool.subagentOptions(ctx, max, pricing, ctxWin, childDepth), nested)
opts, nested)
if ctx.Err() != nil && runErr == nil {
runErr = ctx.Err()
+29
View File
@@ -123,6 +123,35 @@ func TestParallelTasksInjectsWorkspaceContextIntoChildren(t *testing.T) {
}
}
// TestParallelTasksDeliveryClassifiesPristinePrompt pins the trusted
// classifier channel on the parallel_tasks path: delivery intent must be
// judged from the child's pristine prompt, not the workspace-wrapped text.
// The wrapper is long enough that the IsTask length fallback classifies it as
// a task; without ClassifierTaskText a plain conversational child ("Who are
// you?") would be required to produce work receipts it has no reason to earn
// and would exhaust final-answer readiness instead of answering.
func TestParallelTasksDeliveryClassifiesPristinePrompt(t *testing.T) {
workspace := t.TempDir()
task := NewTaskTool(promptRoutingProvider{}, nil, tool.NewRegistry(), 20, 0, 0, 0, 0, 0, 0, 0.0, "", "sys", nil, 0, "", "", nil).
WithTranscripts(NewSubagentStore(t.TempDir()), workspace, "base-model", "base-effort").
WithDeliveryProfile(true)
parallel := NewParallelTasksTool(task, tool.NewRegistry())
ctx := withCallContext(context.Background(), "parallel-call", event.Discard, nil, false)
out, err := parallel.Execute(ctx, json.RawMessage(`{"tasks":[{"prompt":"Who are you?"},{"prompt":"Nice to meet you"}]}`))
if err != nil {
t.Fatalf("Execute: %v", err)
}
if strings.Contains(out, "readiness") {
t.Fatalf("delivery readiness leaked into a conversational parallel child: %q", out)
}
// The echo provider replays the child's full user turn; both children must
// have answered (their prompts echo back with the trailing " ok").
if !strings.Contains(out, "Who are you?") || !strings.Contains(out, "Nice to meet you") || strings.Count(out, " ok") < 2 {
t.Fatalf("parallel output = %q, want both children's answers", out)
}
}
// TestParallelTasksInheritLanguagePreferencesFromContext pins parallel children
// to the same transient language injection the task tool applies: both the
// response- and reasoning-language blocks must reach each child's user turn.
+2 -2
View File
@@ -9,7 +9,7 @@ import (
var reTransientUserBlock = regexp.MustCompile(`(?s)^\s*<(?:response-language|reasoning-language|memory-update|background-jobs|active-goal|hook-context|capability-route)(?:\s+[^>]*)?>.*?</(?:response-language|reasoning-language|memory-update|background-jobs|active-goal|hook-context|capability-route)>\s*\n?`)
// stripTrailingDeliveryRuntime removes the exact delivery-runtime marker the
// agent appends to user turns in delivery mode (agent.go deliveryRuntimeMarker).
// agent appends to user turns in delivery mode (agent.go DeliveryRuntimeMarker).
// Unlike the prefix blocks it trails the user text, so preview/title derivation
// needs a suffix cut — leaving it produced session titles like
// "你是谁? <delivery-run…". The cut is byte-exact rather than a regex: a lazy
@@ -19,7 +19,7 @@ var reTransientUserBlock = regexp.MustCompile(`(?s)^\s*<(?:response-language|rea
// so user messages discussing it carry no host suffix at all.)
func stripTrailingDeliveryRuntime(s string) string {
trimmed := strings.TrimRight(s, " \t\r\n")
if cut, ok := strings.CutSuffix(trimmed, deliveryRuntimeMarker); ok {
if cut, ok := strings.CutSuffix(trimmed, DeliveryRuntimeMarker); ok {
return strings.TrimRight(cut, " \t\r\n")
}
return s