fix(run): address gadfly review of the phases PR
executus CI / test (pull_request) Successful in 48s
executus CI / test (pull_request) Successful in 48s
Real findings from the consensus review (37 raw; many devstral dups/noise): - Optional/budget-salvage branches no longer swallow a context cancellation / deadline / critic-kill: such errors return immediately so the run is classified cancelled/timeout/killed, not "ok" with a fallback. (the most serious finding — an Optional final phase could mask a killed run) - IsRunFunc bare phase now feeds the SHARED step observer (not just the audit recorder), so the critic's activity clock + Result.Steps see it — a long synthesize phase no longer looks idle to the critic. - phaseModel returns the resolver's enriched (usage-attribution) context and the phase's calls use it, mirroring the single-loop path (non-base-tier phases were mis-attributed). - salvagePhaseTranscript trims the tail on a rune boundary (was a raw byte slice that could split a UTF-8 rune); maxSalvage is now a named const with rationale. - expandPhaseTemplate logs a WARN on parse/execute failure instead of silently returning the unexpanded template; documented the phase-name identifier requirement + the "Query" shadow. - removed the dead phaseDeps.baseTier field. - extracted multimodalUserMessage, shared by runAgent + the phase runner (was duplicated image-folding). - aggregated phase usage is stamped onto the result even on a hard-error return; TrimSpace computed once; filterToolbox returns the base toolbox as-is for the empty-names (full-palette) case instead of copying; phaseModel WARN no longer prints error=<nil>. New test: Optional phase does not swallow a cancellation. Full ./... green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+10
-17
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.stevedudenhoeffer.com/steve/majordomo/agent"
|
||||
@@ -344,13 +343,13 @@ func (e *Executor) Run(ctx context.Context, ra RunnableAgent, inv tool.Invocatio
|
||||
// (Per-phase step caps are fixed — the critic's dynamic ceiling is not
|
||||
// propagated to phases — but its steer + hard deadline still apply.)
|
||||
runRes, runErr = e.runPhases(runCtx, ra, phaseDeps{
|
||||
baseModel: model,
|
||||
baseTier: tier,
|
||||
baseToolbox: toolbox,
|
||||
baseMaxIter: maxIter,
|
||||
sharedOpts: sharedOpts,
|
||||
steer: steer,
|
||||
rec: rec,
|
||||
baseModel: model,
|
||||
baseToolbox: toolbox,
|
||||
baseMaxIter: maxIter,
|
||||
sharedOpts: sharedOpts,
|
||||
stepObserver: stepObserver,
|
||||
steer: steer,
|
||||
rec: rec,
|
||||
}, input, inv.Images)
|
||||
}
|
||||
|
||||
@@ -492,15 +491,9 @@ func runAgent(ctx context.Context, ag *agent.Agent, input string, images []llm.I
|
||||
if len(images) == 0 {
|
||||
return ag.Run(ctx, input, opts...)
|
||||
}
|
||||
parts := make([]llm.Part, 0, len(images)+1)
|
||||
if strings.TrimSpace(input) != "" {
|
||||
parts = append(parts, llm.Text(input))
|
||||
}
|
||||
for _, img := range images {
|
||||
parts = append(parts, img)
|
||||
}
|
||||
// Copy opts before appending so a caller-supplied backing array is never
|
||||
// mutated/aliased (the variadic slice can have spare capacity).
|
||||
opts = append(opts[:len(opts):len(opts)], agent.WithHistory([]llm.Message{llm.UserParts(parts...)}))
|
||||
// mutated/aliased (the variadic slice can have spare capacity). The multimodal
|
||||
// opening turn (text + image parts) is built by the shared helper.
|
||||
opts = append(opts[:len(opts):len(opts)], agent.WithHistory([]llm.Message{multimodalUserMessage(input, images)}))
|
||||
return ag.Run(ctx, "", opts...)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user