Files
m 09d53adfd1
CI / test (push) Successful in 1m7s
CI / test (pull_request) Successful in 44s
feat(work): materialize durable solution waves
2026-08-23 16:56:58 +03:00

60 lines
2.8 KiB
Go

package domain
import (
"fmt"
"git.cin.su/m/Assistant/contracts/work"
"testing"
)
func TestSolutionSearchDoesNotDeadEndAfterBudgetedWave(t *testing.T) {
decision, err := DecideSolutionSearch(work.SolutionSearchInput{GoalID: "goal-1", GoalState: work.GoalInProgress, SearchRound: 3, MaxRound: 3, Unresolved: true})
if err != nil {
t.Fatal(err)
}
if !decision.ContinueSearching || decision.State != work.OrchestrationWaiting || decision.NextSearchRound != 4 {
t.Fatalf("unexpected exhausted-wave decision: %#v", decision)
}
if len(decision.Options) != 5 || decision.Options[4] != work.SearchOptionHuman {
t.Fatalf("all search avenues were not retained: %#v", decision.Options)
}
}
func TestSolutionSearchStopsOnlyForCancelOrVerifiedCompletion(t *testing.T) {
completed, err := DecideSolutionSearch(work.SolutionSearchInput{GoalID: "goal-1", GoalState: work.GoalCompleted, VerificationReady: true, MaxRound: 1})
if err != nil || completed.ContinueSearching || completed.State != work.OrchestrationCompleted {
t.Fatalf("verified completion was not terminal: %#v %v", completed, err)
}
cancelled, err := DecideSolutionSearch(work.SolutionSearchInput{GoalID: "goal-1", GoalState: work.GoalInProgress, CancelRequested: true, MaxRound: 1})
if err != nil || cancelled.ContinueSearching || cancelled.State != work.OrchestrationCancelled {
t.Fatalf("cancel was not terminal: %#v %v", cancelled, err)
}
}
func TestBuildSolutionSearchWaveMaterializesParallelAgentDirections(t *testing.T) {
decision, err := DecideSolutionSearch(work.SolutionSearchInput{GoalID: "goal-1", GoalState: work.GoalInProgress, MaxRound: 1})
if err != nil {
t.Fatal(err)
}
options := []work.SearchOptionKind{work.SearchOptionAgents, work.SearchOptionCapability, work.SearchOptionResearch, work.SearchOptionReflection}
dispatches := make([]work.SolutionSearchDispatch, 0, len(options))
for i, option := range options {
dispatches = append(dispatches, work.SolutionSearchDispatch{Option: option, Intent: work.AgentDispatchIntent{DispatchID: workDispatchID(i), IdempotencyKey: workDispatchID(i) + ":idem"}})
}
graph, intents, err := BuildSolutionSearchWave(decision, work.SolutionSearchWaveRequest{GraphID: "graph-1", IdempotencyPrefix: "goal-1", Dispatches: dispatches, Budget: work.GraphBudget{MaxParallel: 4, MaxAttempts: 2}})
if err != nil {
t.Fatal(err)
}
if len(graph.Nodes) != 4 || len(intents) != 4 || graph.State != work.GraphDraft {
t.Fatalf("unexpected materialized wave: %#v %#v", graph, intents)
}
for i, node := range graph.Nodes {
if node.State != work.GraphNodeBlocked || intents[i].SpecialistPurpose != string(options[i]) {
t.Fatalf("wave direction not preserved: node=%#v intent=%#v", node, intents[i])
}
}
}
func workDispatchID(index int) string {
return fmt.Sprintf("dispatch-%d", index)
}