package usecase import ( "context" "go.maximumdirect.net/promptkit/internal/domain" ) // ConversationTurn is one delivery to a coordinator callback. type ConversationTurn[R any] struct { Exchange *domain.Exchange Result *R RemainingTurns int } // CoordinationOutcome describes a normal coordinator termination. type CoordinationOutcome string const ( CoordinationStopped CoordinationOutcome = "stopped" CoordinationExhausted CoordinationOutcome = "exhausted" ) // CoordinationResult holds a normal outcome or a completed undelivered result. type CoordinationResult[R any] struct { Outcome CoordinationOutcome Undelivered *R } // Coordinate advances one branch using the supplied callback and ordinary run. func Coordinate[Q, R any](ctx context.Context, start *domain.Exchange, turns int, next func(context.Context, ConversationTurn[R]) (*Q, error), run func(context.Context, *domain.Exchange, *Q) (*R, *domain.Exchange, error), ) (CoordinationResult[R], error) { current := start var pending *R for { if err := ctx.Err(); err != nil { return CoordinationResult[R]{Undelivered: pending}, err } request, err := next(ctx, ConversationTurn[R]{Exchange: current, Result: pending, RemainingTurns: turns}) pending = nil if contextErr := ctx.Err(); contextErr != nil { return CoordinationResult[R]{}, contextErr } if err != nil { return CoordinationResult[R]{}, err } if request == nil { return CoordinationResult[R]{Outcome: CoordinationStopped}, nil } if turns == 0 { return CoordinationResult[R]{Outcome: CoordinationExhausted}, nil } if err := ctx.Err(); err != nil { return CoordinationResult[R]{}, err } turns-- var nextExchange *domain.Exchange pending, nextExchange, err = run(ctx, current, request) if err != nil { return CoordinationResult[R]{}, err } current = nextExchange } }