Files
Agent-Native/exec/step_retry_budget_test.go
weichen dd2ac11b31 Extend exec.Request with capability fields and enforce budgets end to end
Why: the frozen exec port could not express per-request model, tool
allowlist, skills, budget, or identifiers, so squad roles and product
profiles had no way to steer one execution; contract v0.1.0 semantics
(budget/cancel) stayed unconsumed (parity-matrix #4/#10/#16).

Evidence: go vet ./... clean; go test ./... all ok, including new
loop budget caps (tool calls, prompt tokens, deadline), cancel
propagation into a blocking tool, registry Restrict fail-closed
policy tests, native adapter v2 tests (model override, allowlist,
deadline-bound context, execution-id log header, skills prompt line),
and settle classification marking budget_exceeded permanent.

Impact: exec.Request gains optional Model/ToolAllowlist/Skills/Budget/
SessionScope/ExecutionID/Env (zero value = legacy behavior); runtime
loop enforces a shared budget and emits budget_exceeded events;
adapters/native consumes all new fields. Consumers on the old port
keep compiling and behaving identically.

Refs: docs/parity-matrix.md rows #4/#10/#16 (Batch2 scope); deps/
development-guidelines standards/agent-execution.md (shared budget
across retry layers).
2026-10-01 05:26:19 +00:00

38 lines
1.0 KiB
Go

package exec
import (
"errors"
"strings"
"testing"
"time"
)
func TestAssessStepFailureBudgetExceededIsPermanent(t *testing.T) {
ass := AssessStepFailure(StepFailureSignals{
Err: errors.New("budget_exceeded: tool calls 12 > max 10"),
})
if ass.Class != FailurePermanent {
t.Fatalf("budget exhaustion must classify permanent (no auto retry), got %s", ass.Class)
}
if !strings.Contains(ass.Reason, "permanent") {
t.Fatalf("want permanent reason, got %s", ass.Reason)
}
}
func TestAssessStepFailureDeadlinePastIsPermanent(t *testing.T) {
ass := AssessStepFailure(StepFailureSignals{
Result: &Result{ErrorReason: "budget_exceeded: deadline 2026-10-01T00:00:00Z passed"},
})
if ass.Class != FailurePermanent {
t.Fatalf("deadline exhaustion must classify permanent, got %s", ass.Class)
}
}
func TestBudgetZeroValueUnlimited(t *testing.T) {
var b Budget
if b.MaxPromptTokens != 0 || b.MaxCompletionTokens != 0 || b.MaxToolCalls != 0 || !b.Deadline.IsZero() {
t.Fatal("zero budget must express unlimited")
}
_ = time.Now
}