128 lines
5.0 KiB
Go
128 lines
5.0 KiB
Go
package engine
|
|
|
|
import "context"
|
|
|
|
// RunQueueAdapter is the engine's contract with the runs queue. The Phase 2
|
|
// final integration emits QueueRun requests through this interface; Phase 3
|
|
// implements it against the runs table (renamed from office_runs).
|
|
//
|
|
// Implementations MUST be safe for concurrent use and SHOULD treat
|
|
// (IdempotencyKey) as a uniqueness key when set: if a request with the same
|
|
// idempotency key has already been queued the implementation returns nil
|
|
// without enqueuing a duplicate.
|
|
type RunQueueAdapter interface {
|
|
QueueRun(ctx context.Context, req QueueRunRequest) error
|
|
}
|
|
|
|
// QueueRunRequest is the typed payload the engine hands to RunQueueAdapter.
|
|
//
|
|
// AgentProfileID is always populated — the engine resolves the action's
|
|
// Target string into a concrete agent profile id before invoking the
|
|
// adapter. TaskID is similarly resolved (defaulting to the trigger's
|
|
// task id when the action specifies "this" or leaves it blank).
|
|
type QueueRunRequest struct {
|
|
AgentProfileID string
|
|
TaskID string
|
|
WorkflowStepID string
|
|
Reason string
|
|
IdempotencyKey string
|
|
Payload map[string]any
|
|
}
|
|
|
|
// ParticipantInfo is a lightweight projection of a workflow_step_participants
|
|
// row — enough for the engine's resolver/quorum logic without coupling the
|
|
// engine package to the workflow models package.
|
|
//
|
|
// TaskID is "" for template-level rows and non-empty for per-task overrides.
|
|
type ParticipantInfo struct {
|
|
ID string
|
|
StepID string
|
|
TaskID string
|
|
Role string
|
|
AgentProfileID string
|
|
DecisionRequired bool
|
|
Position int
|
|
}
|
|
|
|
// DecisionInfo is a lightweight projection of a workflow_step_decisions row.
|
|
type DecisionInfo struct {
|
|
ID string
|
|
TaskID string
|
|
StepID string
|
|
ParticipantID string
|
|
Decision string
|
|
Note string
|
|
}
|
|
|
|
// ParticipantStore reads the workflow_step_participants table for an engine
|
|
// step + task. Wave 8 of the task-model-unification ADR introduced
|
|
// dual-scoped participants: rows with task_id=” apply to every task at the
|
|
// step (template-level), rows with task_id != ” apply only to that task
|
|
// (per-task override). Per-task rows take precedence on (role, agent) ties.
|
|
//
|
|
// The taskID argument is the trigger's task; implementations MUST merge
|
|
// template-level rows with per-task rows for that task and return the
|
|
// resolved set. Returning nil/empty list is valid and signals a
|
|
// single-agent step for that task.
|
|
type ParticipantStore interface {
|
|
ListStepParticipants(ctx context.Context, stepID, taskID string) ([]ParticipantInfo, error)
|
|
}
|
|
|
|
// DecisionStore reads and writes workflow_step_decisions rows. The engine
|
|
// uses it from the wait_for_quorum guard, the clear_decisions action, and
|
|
// Engine.RecordParticipantDecision.
|
|
type DecisionStore interface {
|
|
ListStepDecisions(ctx context.Context, taskID, stepID string) ([]DecisionInfo, error)
|
|
RecordStepDecision(ctx context.Context, d DecisionInfo) error
|
|
ClearStepDecisions(ctx context.Context, taskID, stepID string) (int64, error)
|
|
}
|
|
|
|
// CEOAgentResolver resolves the workspace's CEO agent profile for the
|
|
// "workspace.ceo_agent" QueueRun target. Implementations look the workspace
|
|
// up via the trigger's task. Phase 2 final exposes the contract; office
|
|
// integration provides the implementation.
|
|
type CEOAgentResolver interface {
|
|
ResolveCEOAgentProfileID(ctx context.Context, taskID string) (string, error)
|
|
}
|
|
|
|
// ChildTaskSpec is the typed payload TaskCreator receives. The engine
|
|
// resolves blank fields to defaults (parent task workflow / first runnable
|
|
// step / inherited assignee) inside the adapter — keeping the engine
|
|
// package free of model imports.
|
|
type ChildTaskSpec struct {
|
|
Title string
|
|
Description string
|
|
WorkflowID string
|
|
StepID string
|
|
AgentProfileID string
|
|
}
|
|
|
|
// TaskCreator is the engine's contract with whoever knows how to create a
|
|
// task with a parent. Defined here so CreateChildTaskCallback can stay in
|
|
// the engine package; the office side implements the interface against
|
|
// the task service.
|
|
//
|
|
// Implementations MUST:
|
|
//
|
|
// 1. Set parent_id to parentTaskID on the new task.
|
|
// 2. Persist the new task with the supplied workflow + step + assignee.
|
|
// 3. Return the new task id (non-empty) or a non-nil error.
|
|
//
|
|
// The engine does not call CreateChildTask in EvaluateOnly mode — it
|
|
// always commits.
|
|
type TaskCreator interface {
|
|
CreateChildTask(ctx context.Context, parentTaskID string, spec ChildTaskSpec) (taskID string, err error)
|
|
}
|
|
|
|
// WorkflowSwitcher is the engine's contract for in-place workflow swap.
|
|
// Implementations mutate tasks.workflow_id and tasks.workflow_step_id and
|
|
// return the resolved step id (defaulting blank stepID to the workflow's
|
|
// first runnable step).
|
|
//
|
|
// The engine drives on_exit on the old step before the swap and on_enter
|
|
// on the new step after — the implementation is responsible only for the
|
|
// row update.
|
|
type WorkflowSwitcher interface {
|
|
SwitchTaskWorkflow(ctx context.Context, taskID, newWorkflowID, newStepID string) (resolvedStepID string, err error)
|
|
}
|