Documentation
¶
Index ¶
- Constants
- Variables
- func DetectCodex(binaryPath string) (string, bool)
- func GovernedWorkflowStepInstructions(taskRef string) []string
- func ImplementationTaskCloseoutInstructions(taskRef string, filesToEdit []string) []string
- func IsExternalRunnerKind(kind string) bool
- func IsTerminalRunStatus(status string) bool
- func RenderCodexTaskPrompt(input CodexTaskInput) string
- func RunnerFailureCategoryIsHardBlocker(category string) bool
- type Automation
- type AutomationAgent
- type AutomationAttempt
- type AutomationDeleteCounts
- type AutomationFilter
- type AutomationParallelBatch
- type AutomationRun
- type CancelRunInput
- type ClaimNextRunInput
- type ClaimedRun
- type CodexCommand
- type CodexCommandInput
- type CodexRunResult
- type CodexTaskInput
- type CommandSpec
- type CompleteAttemptInput
- type ComputeParallelBatchInput
- type ConfidenceGovernance
- type CreateAutomationInput
- type CreateRemediationFromFindingInput
- type CreateRemediationFromFindingResult
- type DirtyScopeRecoveryOptions
- type EvidenceGovernance
- type Executor
- type ExecutorOptions
- type GovernanceActionInput
- type GovernanceConfidenceInput
- type GovernanceKnowledgeCandidateInput
- type GovernanceOptions
- type GovernanceOutcomeInput
- type HeartbeatRunInput
- type KnowledgeGovernance
- type Options
- type PermissionCheckInput
- type PermissionResolver
- type PermissionSnapshotMetadata
- type ResetGitOpsRecoveryInput
- type RunCondition
- type RunFilter
- type Service
- func (svc *Service) CallAutomationTool(ctx context.Context, name string, arguments json.RawMessage) (any, error)
- func (svc *Service) CancelAutomationsForPlan(ctx context.Context, projectID string, planID string) error
- func (svc *Service) CancelOrphanedAutomations(ctx context.Context, projectID string, activePlanIDs map[string]bool)
- func (svc *Service) CancelRun(ctx context.Context, input CancelRunInput) (AutomationRun, error)
- func (svc *Service) ClaimNextRun(ctx context.Context, input ClaimNextRunInput) (ClaimedRun, error)
- func (svc *Service) CompleteAttempt(ctx context.Context, input CompleteAttemptInput) (AutomationRun, error)
- func (svc *Service) ComputeParallelBatch(ctx context.Context, input ComputeParallelBatchInput) (AutomationParallelBatch, error)
- func (svc *Service) CreateAutomation(ctx context.Context, input CreateAutomationInput) (Automation, error)
- func (svc *Service) CreateRemediationFromFinding(ctx context.Context, input CreateRemediationFromFindingInput) (CreateRemediationFromFindingResult, error)
- func (svc *Service) DeleteAutomationsForPlanCascade(ctx context.Context, projectID, planID string) (AutomationDeleteCounts, error)
- func (svc *Service) ExecuteQueuedRun(ctx context.Context, projectID, runID string) (AutomationRun, error)
- func (svc *Service) ForceCancelRun(ctx context.Context, input CancelRunInput) (AutomationRun, error)
- func (svc *Service) GetAutomation(ctx context.Context, projectID, automationID string) (Automation, error)
- func (svc *Service) GetRun(ctx context.Context, projectID, runID string) (AutomationRun, error)
- func (svc *Service) HandleWorkPlanStatusChanged(ctx context.Context, event projectworkplan.WorkPlanStatusChange) error
- func (svc *Service) HeartbeatRun(ctx context.Context, input HeartbeatRunInput) (AutomationRun, error)
- func (svc *Service) ListAutomations(ctx context.Context, filter AutomationFilter) ([]Automation, error)
- func (svc *Service) ListRuns(ctx context.Context, filter RunFilter) ([]AutomationRun, error)
- func (svc *Service) ListRunsForStats(ctx context.Context, filter RunFilter) ([]AutomationRun, error)
- func (svc *Service) PurgeCancelledAutomations(ctx context.Context, projectID, status string) (int, error)
- func (svc *Service) PurgeTerminalRuns(ctx context.Context, projectID, status string) (int, error)
- func (svc *Service) ReconcileReadyAutomationsForPlan(ctx context.Context, projectID string, planID string) error
- func (svc *Service) ResetGitOpsRecovery(ctx context.Context, input ResetGitOpsRecoveryInput) (AutomationRun, error)
- func (svc *Service) RunNow(ctx context.Context, input SubmitRunInput) (AutomationRun, error)
- func (svc *Service) SetClaimIdleSleep(sleeper func())
- func (svc *Service) SetWorkflowChainGitOpsRetrier(retrier func(context.Context, string, string) error)
- func (svc *Service) SetWorkflowChainPRRefRecorder(recorder workflowChainPRRefRecorder)
- func (svc *Service) StartAutomationReconciler(ctx context.Context)
- func (svc *Service) StartLeaseReaper(ctx context.Context)
- func (svc *Service) StartReviewedCloseoutReconciler(ctx context.Context)
- func (svc *Service) StartZombieReviewReaper(ctx context.Context)
- func (svc *Service) SubmitRun(ctx context.Context, input SubmitRunInput) (AutomationRun, error)
- func (svc *Service) UpdateAutomationStatus(ctx context.Context, input UpdateAutomationStatusInput) (Automation, error)
- type Store
- type SubmitRunInput
- type UpdateAutomationStatusInput
- type WorkPlanStatusTriggerOptions
- type WorkTaskAPI
Constants ¶
const ( GovernanceActionKindAutomationRunStart = "automation_run_start" GovernanceOutcomeKindAutomationAttempt = "automation_attempt" )
const ( AutomationStatusDraft = "draft" AutomationStatusEnabled = "enabled" AutomationStatusDisabled = "disabled" AutomationStatusRunning = "running" AutomationStatusPaused = "paused" AutomationStatusFailed = "failed" AutomationStatusCancelled = "cancelled" AutomationStatusSuperseded = "superseded" )
const ( RunStatusQueued = "queued" RunStatusClaiming = "claiming" RunStatusStarting = "starting" RunStatusRunning = "running" RunStatusVerifying = "verifying" RunStatusCompleted = "completed" RunStatusFailed = "failed" RunStatusBlocked = "blocked" RunStatusCancelled = "cancelled" RunStatusPolicyDenied = "policy_denied" RunStatusTimeout = "timeout" )
const ( BatchStatusPlanned = "planned" BatchStatusRunning = "running" BatchStatusCompleted = "completed" BatchStatusFailed = "failed" BatchStatusBlocked = "blocked" BatchStatusCancelled = "cancelled" )
const ( RunnerKindCodexCLI = "codex_cli" RunnerKindCrushCLI = "crush_cli" RunnerKindManual = "manual" )
const ( RunnerExecutionInProcess = "in_process" RunnerExecutionExternal = "external" RunnerExecutionManaged = "managed" )
const ( TriggerKindManual = "manual" TriggerKindAutomatic = "automatic" )
const ( AutomationSourceManual = "manual" AutomationSourceWorkflow = "workflow" )
const DefaultClaimIdleSleepInterval = 200 * time.Millisecond
DefaultClaimIdleSleepInterval bounds the claim-loop spin rate when a full recovery cycle claims nothing (Bug #4). Caps the worst case at ~5 Hz so that even an un-memoized recovery path cannot re-pin the CPU at ~184 Hz. The memoization fix (RunSafeSummaryGitOpsPostTaskNonRecoverable) is the primary remedy; this sleep is defense-in-depth. Exported so the server binary wires the canonical interval via SetClaimIdleSleep.
const PermissionSnapshotRefPrefix = "permission_snapshot:"
const RunSafeSummaryGitOpsPostTaskNonRecoverable = "gitops_post_task_non_recoverable"
RunSafeSummaryGitOpsPostTaskNonRecoverable marks a failed run that the GitOps post-task recovery scan (claimGitOpsPostTaskRecovery) has determined is NOT recoverable (RunnerKind != codex_cli, or a failure category outside isRecoverableGitOpsPostTaskFailure). Bug #4: without this marker the scan re-evaluated and re-logged such runs on EVERY claim cycle (~184 Hz), pinning the CPU and starving the reconciler. The first scan stamps this marker so subsequent cycles skip the run with a single SafeSummary check — no re-list, re-filter, re-evaluate, or re-log. Cleared by operator recovery (reset_recovery / retry paths that rewrite SafeSummary), so a reopened run gets a fresh evaluation. The value is stable and must not change.
const RunSafeSummaryGitOpsPostTaskRecovery = "gitops_post_task_recovery"
const RunSafeSummaryGitOpsRecoveryRequeuedImplementation = "gitops_recovery_requeued_implementation"
const RunSafeSummaryPostImplementationReviewQueued = "post_implementation_review_queued"
const RunSafeSummaryVerifiedTaskDone = "external_codex_cli_verified_task_done"
const RunSafeSummaryWorkflowChainGitOpsRepairRetryFailed = "workflow_chain_gitops_repair_retry_failed"
RunSafeSummaryWorkflowChainGitOpsRepairRetryFailed marks a run that reached the post-repair GitOps retry step and could not proceed (e.g. the chain was not ready for retry, or verification failed again). It is a terminal closeout: the parent repair task is already Done by the time this is written, and the run is not recoverable through the standard GitOps post-task recovery scan. The recovery claim and reconcile paths recognize this marker and skip the run silently instead of re-evaluating it every cycle. The value is stable and must not change: existing runs in stores already carry the bare literal "workflow_chain_gitops_repair_retry_failed".
Variables ¶
var ErrClaimRace = errors.New("automation run claim lost race")
ErrClaimRace signals that a ClaimRun conditional write lost the race: the run was no longer in RunStatusQueued when the write was attempted (another claimer transitioned it first). Callers treat it as a benign lost race and advance to the next candidate, mirroring claimGitOpsPostTaskRecovery's sameRecoveryClaim handling. It is the store-level primitive that makes the Queued->Claiming claim transition correct without holding the per-project claim mutex across the write.
var ErrConditionMismatch = errors.New("automation run condition mismatch")
ErrConditionMismatch signals that a UpdateRunIf conditional write did not apply: the persisted run's Status or ClaimID no longer matched the expected condition (e.g. a concurrent claim bumped ClaimID). Callers treat it as a benign lost race and skip the write, mirroring requeueAbandonedRunningRun's GetRun+identity-check guard. It is the store-level primitive the reconcile terminal-write guards use to avoid clobbering a concurrently-claimed run.
var ErrInvalidInput = errors.New("invalid project automation input")
Functions ¶
func DetectCodex ¶
func GovernedWorkflowStepInstructions ¶ added in v0.3.0
func ImplementationTaskCloseoutInstructions ¶ added in v0.3.0
func IsExternalRunnerKind ¶ added in v0.3.0
IsExternalRunnerKind reports whether kind is an externally-executed runner kind (one backed by an out-of-process harness like codex or crush, claimed and reported through ClaimNextRun/CompleteAttempt). RunnerKindManual and any unknown kind return false. This is the single allowlist used by the claim guard, CompleteAttempt gate, and queue filter so all three stay in sync as new external harnesses are added.
func IsTerminalRunStatus ¶ added in v0.3.0
IsTerminalRunStatus reports whether the given automation run status is terminal (no further state transitions expected). Exported so the store layer can use it for purge safety guards.
func RenderCodexTaskPrompt ¶ added in v0.2.1
func RenderCodexTaskPrompt(input CodexTaskInput) string
func RunnerFailureCategoryIsHardBlocker ¶ added in v0.3.1
RunnerFailureCategoryIsHardBlocker reports whether a failure category denotes a permanent (non-code-repairable) failure — a config, permission, or auth problem a validation-reviewer repair task cannot fix. It is the single source of truth shared by the projectautomation recovery classifier and the chain-layer repairability classifier, so the two layers cannot drift and re-introduce a cross-layer repair loop (stuck-runs audit 2026-06-22 Problem 3, review x2 F4/F7).
Types ¶
type Automation ¶
type Automation struct {
ID string `json:"id"`
ProjectID string `json:"project_id"`
AutomationRef string `json:"automation_ref"`
Title string `json:"title"`
Purpose string `json:"purpose"`
Status string `json:"status"`
AgentID string `json:"agent_id"`
PlanID string `json:"plan_id,omitempty"`
AllowedTaskRefs []string `json:"allowed_task_refs,omitempty"`
RequiredReviewTaskIDs []string `json:"required_review_task_ids,omitempty"`
TriggerKind string `json:"trigger_kind"`
SourceKind string `json:"source_kind,omitempty"`
SchedulePolicy string `json:"schedule_policy,omitempty"`
PermissionRef string `json:"permission_ref"`
CreatedByRunID string `json:"created_by_run_id,omitempty"`
TraceID string `json:"trace_id,omitempty"`
// Harness/Model/Provider carry the per-agent harness selection from the
// workflow definition through to the runner. Empty means inherit the
// server/runner default (codex). Populated by the workflow compiler from
// the [[agents]] block; read by claimedRunForAutomation to surface on the
// ClaimedRun response so the runner can dispatch per-run.
Harness string `json:"harness,omitempty"`
Model string `json:"model,omitempty"`
Provider string `json:"provider,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type AutomationAgent ¶
type AutomationAgent struct {
ID string `json:"id"`
DisplayName string `json:"display_name"`
Purpose string `json:"purpose"`
Enabled bool `json:"enabled"`
AllowedSkills []string `json:"allowed_skills,omitempty"`
AllowedTools []string `json:"allowed_tools,omitempty"`
AllowedCommands []CommandSpec `json:"allowed_commands,omitempty"`
DeniedCommands []string `json:"denied_commands,omitempty"`
WorkspaceMode string `json:"workspace_mode"`
NetworkPolicy string `json:"network_policy"`
SecretPolicy string `json:"secret_policy"`
LogPolicy string `json:"log_policy"`
MaxRuntime time.Duration `json:"max_runtime"`
MaxRetries int `json:"max_retries"`
}
type AutomationAttempt ¶
type AutomationAttempt struct {
ID string `json:"id"`
ProjectID string `json:"project_id"`
AutomationRunID string `json:"automation_run_id"`
AttemptNumber int `json:"attempt_number"`
RunnerKind string `json:"runner_kind"`
CommandRef string `json:"command_ref,omitempty"`
InputSummaryHash string `json:"input_summary_hash,omitempty"`
OutputSummaryHash string `json:"output_summary_hash,omitempty"`
Status string `json:"status"`
FailureCategory string `json:"failure_category,omitempty"`
DurationMS int64 `json:"duration_ms"`
VerifierResultRefs []string `json:"verifier_result_refs,omitempty"`
EvidenceRefs []string `json:"evidence_refs,omitempty"`
ClaimRefs []string `json:"claim_refs,omitempty"`
KnowledgeRefs []string `json:"knowledge_candidate_refs,omitempty"`
CreatedAt time.Time `json:"created_at"`
FinishedAt time.Time `json:"finished_at,omitempty"`
}
type AutomationDeleteCounts ¶ added in v0.3.0
AutomationDeleteCounts tallies what an automation plan-cascade delete removed.
type AutomationFilter ¶
type AutomationParallelBatch ¶
type AutomationParallelBatch struct {
ID string `json:"id"`
ProjectID string `json:"project_id"`
AutomationRunID string `json:"automation_run_id"`
OrchestratorRunID string `json:"orchestrator_run_id"`
PlanID string `json:"plan_id"`
TaskIDs []string `json:"task_ids"`
Status string `json:"status"`
SafetyReason string `json:"safety_reason"`
ConflictSummary string `json:"conflict_summary,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type AutomationRun ¶
type AutomationRun struct {
ID string `json:"id"`
ProjectID string `json:"project_id"`
AutomationID string `json:"automation_id"`
AgentID string `json:"agent_id"`
PlanID string `json:"plan_id"`
TaskID string `json:"task_id"`
WorkTaskStatus string `json:"work_task_status"`
Status string `json:"status"`
RunnerKind string `json:"runner_kind"`
AgentRunID string `json:"agent_run_id,omitempty"`
TraceID string `json:"trace_id,omitempty"`
AttemptCount int `json:"attempt_count"`
OrchestratorRunID string `json:"orchestrator_run_id,omitempty"`
ParentRunID string `json:"parent_run_id,omitempty"`
WorkerRunIDs []string `json:"worker_run_ids,omitempty"`
ParallelGroupID string `json:"parallel_group_id,omitempty"`
FailureCategory string `json:"failure_category,omitempty"`
SafeSummary string `json:"safe_summary,omitempty"`
// OperatorDiagnostics carries unsanitized failure detail (absolute paths, git
// commands, refs) populated by the runner on failure. It is OPERATOR-ONLY: the
// MCP boundary (mcp_adapter.go) projects it out of agent responses unless the
// caller passes include_diagnostics=true. Agents never see it by default, so
// the privacy boundary (no roots/paths/commands to agents) is preserved.
OperatorDiagnostics string `json:"operator_diagnostics,omitempty"`
ClaimID string `json:"claim_id,omitempty"`
RunnerID string `json:"runner_id,omitempty"`
ClaimedAt time.Time `json:"claimed_at,omitempty"`
LastHeartbeatAt time.Time `json:"last_heartbeat_at,omitempty"`
LeaseExpiresAt time.Time `json:"lease_expires_at,omitempty"`
StartedAt time.Time `json:"started_at,omitempty"`
FinishedAt time.Time `json:"finished_at,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type CancelRunInput ¶ added in v0.3.0
type ClaimNextRunInput ¶
type ClaimedRun ¶
type ClaimedRun struct {
Run AutomationRun `json:"run"`
Automation *Automation `json:"automation,omitempty"`
CodexInput CodexTaskInput `json:"codex_input"`
TimeoutMS int64 `json:"timeout_ms"`
// AgentHarness/AgentModel/AgentProvider carry the per-agent harness
// selection from the workflow definition, resolved at claim time from the
// automation row. Empty means the runner uses its startup default. The
// runner reads these to dispatch the claimed run to the correct adapter
// (codex or crush) with the right model/provider — Phase 3 per-agent
// routing.
AgentHarness string `json:"agent_harness,omitempty"`
AgentModel string `json:"agent_model,omitempty"`
AgentProvider string `json:"agent_provider,omitempty"`
}
type CodexCommand ¶
type CodexCommand struct {
Path string
Args []string
Env []string
StdinFile string
Dir string
Timeout time.Duration
}
func BuildCodexCommand ¶
func BuildCodexCommand(input CodexCommandInput) (CodexCommand, error)
type CodexCommandInput ¶
type CodexRunResult ¶
type CodexRunResult struct {
ExitCode int
Duration time.Duration
TimedOut bool
OutputTruncated bool
SafeFailureCategory string
Output string
}
func RunCodexCommand ¶
func RunCodexCommand(ctx context.Context, command CodexCommand, maxOutputBytes int64) (CodexRunResult, error)
type CodexTaskInput ¶
type CodexTaskInput struct {
SchemaVersion int `json:"schema_version"`
ProjectID string `json:"project_id"`
MCPServerURL string `json:"mcp_server_url,omitempty"`
AutomationRunID string `json:"automation_run_id"`
TraceID string `json:"trace_id,omitempty"`
PlanID string `json:"plan_id"`
TaskID string `json:"task_id"`
TaskRef string `json:"task_ref"`
Title string `json:"title"`
Description string `json:"description,omitempty"`
EvidenceNeeded []string `json:"evidence_needed,omitempty"`
ContextPackRefs []string `json:"context_pack_refs,omitempty"`
WorkPlanContext []string `json:"work_plan_context,omitempty"`
DependencyContext []string `json:"dependency_context,omitempty"`
FilesToRead []string `json:"files_to_read,omitempty"`
LikelyFilesAffected []string `json:"likely_files_affected,omitempty"`
VerificationRequirement string `json:"verification_requirement"`
ExpectedOutput string `json:"expected_output,omitempty"`
FailureCriteria string `json:"failure_criteria,omitempty"`
ResumeInstructions string `json:"resume_instructions,omitempty"`
AcceptanceCriteria []string `json:"acceptance_criteria,omitempty"`
StopConditions []string `json:"stop_conditions,omitempty"`
VerifierLadder []string `json:"verifier_ladder,omitempty"`
RegressionApplicability string `json:"regression_test_applicability,omitempty"`
DownstreamImpactRefs []string `json:"downstream_impact_refs,omitempty"`
OutputContract string `json:"output_contract,omitempty"`
RunnerInstructions []string `json:"runner_instructions"`
}
type CommandSpec ¶
type CompleteAttemptInput ¶
type CompleteAttemptInput struct {
ProjectID string `json:"project_id,omitempty"`
RunID string `json:"run_id"`
ClaimID string `json:"claim_id,omitempty"`
RunnerID string `json:"runner_id,omitempty"`
Status string `json:"status"`
FailureCategory string `json:"failure_category,omitempty"`
DurationMS int64 `json:"duration_ms,omitempty"`
VerifierResultRefs []string `json:"verifier_result_refs,omitempty"`
EvidenceRefs []string `json:"evidence_refs,omitempty"`
ClaimRefs []string `json:"claim_refs,omitempty"`
ReviewRefs []string `json:"review_result_refs,omitempty"`
KnowledgeRefs []string `json:"knowledge_candidate_refs,omitempty"`
}
type ComputeParallelBatchInput ¶
type ComputeParallelBatchInput struct {
ProjectID string `json:"project_id,omitempty"`
AutomationRunID string `json:"automation_run_id,omitempty"`
OrchestratorRunID string `json:"orchestrator_run_id,omitempty"`
PlanID string `json:"plan_id,omitempty"`
TaskIDs []string `json:"task_ids,omitempty"`
MaxTasks int `json:"max_tasks,omitempty"`
}
type ConfidenceGovernance ¶ added in v0.2.0
type ConfidenceGovernance interface {
RecordConfidenceRef(context.Context, GovernanceConfidenceInput) (string, error)
}
type CreateAutomationInput ¶
type CreateAutomationInput struct {
ProjectID string `json:"project_id,omitempty"`
AutomationRef string `json:"automation_ref"`
Title string `json:"title"`
Purpose string `json:"purpose"`
Status string `json:"status,omitempty"`
AgentID string `json:"agent_id"`
PlanID string `json:"plan_id,omitempty"`
AllowedTaskRefs []string `json:"allowed_task_refs,omitempty"`
RequiredReviewTaskIDs []string `json:"required_review_task_ids,omitempty"`
TriggerKind string `json:"trigger_kind,omitempty"`
SchedulePolicy string `json:"schedule_policy,omitempty"`
PermissionRef string `json:"permission_ref"`
SourceKind string `json:"source_kind,omitempty"`
CreatedByRunID string `json:"created_by_run_id,omitempty"`
TraceID string `json:"trace_id,omitempty"`
Harness string `json:"harness,omitempty"`
Model string `json:"model,omitempty"`
Provider string `json:"provider,omitempty"`
}
type CreateRemediationFromFindingInput ¶ added in v0.2.4
type CreateRemediationFromFindingInput struct {
ProjectID string `json:"project_id,omitempty"`
FindingRef string `json:"finding_ref"`
FindingStatus string `json:"finding_status"`
Title string `json:"title"`
Summary string `json:"summary"`
Severity string `json:"severity,omitempty"`
OwnerAgent string `json:"owner_agent,omitempty"`
ImplementationAgentID string `json:"implementation_agent_id,omitempty"`
CreatedByRunID string `json:"created_by_run_id,omitempty"`
TraceID string `json:"trace_id,omitempty"`
PermissionSnapshotRef string `json:"permission_snapshot_ref,omitempty"`
GitBaseRef string `json:"git_base_ref,omitempty"`
GitBranchRef string `json:"git_branch_ref,omitempty"`
GitWorktreeRef string `json:"git_worktree_ref,omitempty"`
FilesToRead []string `json:"files_to_read,omitempty"`
FilesToEdit []string `json:"files_to_edit,omitempty"`
LikelyFilesAffected []string `json:"likely_files_affected,omitempty"`
EvidenceRefs []string `json:"evidence_refs,omitempty"`
VerificationRequirement string `json:"verification_requirement"`
ReviewGate string `json:"review_gate,omitempty"`
ActivatePlan bool `json:"activate_plan,omitempty"`
}
type CreateRemediationFromFindingResult ¶ added in v0.2.4
type CreateRemediationFromFindingResult struct {
WorkPlan projectworkplan.WorkPlan `json:"work_plan"`
WorkTask projectworkplan.WorkTask `json:"work_task"`
ReviewTask projectworkplan.WorkTask `json:"review_task,omitempty"`
Automation Automation `json:"automation"`
ReviewAutomation Automation `json:"review_automation,omitempty"`
Activated bool `json:"activated"`
}
type DirtyScopeRecoveryOptions ¶ added in v0.2.4
type EvidenceGovernance ¶ added in v0.2.0
type EvidenceGovernance interface {
CreateActionRef(context.Context, GovernanceActionInput) (string, error)
CreateOutcomeRef(context.Context, GovernanceOutcomeInput) (string, error)
}
type Executor ¶ added in v0.2.0
type Executor struct {
// contains filtered or unexported fields
}
func NewExecutor ¶ added in v0.2.0
func NewExecutor(service *Service, options ExecutorOptions) *Executor
type ExecutorOptions ¶ added in v0.2.0
type GovernanceActionInput ¶ added in v0.2.0
type GovernanceConfidenceInput ¶ added in v0.2.0
type GovernanceKnowledgeCandidateInput ¶ added in v0.2.0
type GovernanceOptions ¶ added in v0.2.0
type GovernanceOptions struct {
Evidence EvidenceGovernance
Confidence ConfidenceGovernance
Knowledge KnowledgeGovernance
}
type GovernanceOutcomeInput ¶ added in v0.2.0
type HeartbeatRunInput ¶ added in v0.2.4
type KnowledgeGovernance ¶ added in v0.2.0
type KnowledgeGovernance interface {
CreateCandidateRef(context.Context, GovernanceKnowledgeCandidateInput) (string, error)
}
type Options ¶
type Options struct {
Enabled bool
RunnerEnabled bool
RequireCodexWhenAvailable bool
AllowManualRunner bool
RunnerExecution string
MaxParallelTasks int
DefaultMaxRuntime time.Duration
ExternalRunLeaseTTL time.Duration
DefaultMaxRetries int
RecoveryMaxReplacementRunsPerTask int
// ReworkBudgetPerTask is the maximum number of rework cycles a task may
// undergo before it transitions to failed with outcome
// rework_budget_exhausted. Defaults to 3 (defaultReworkBudgetPerTask). This
// budget is INDEPENDENT from RecoveryMaxReplacementRunsPerTask: rework
// exhaustion does not consume the fatal-retry budget (R5 partition).
ReworkBudgetPerTask int
// ClaimFullReconcileCooldown bounds how often a runner performs a full
// reconciliation sweep during ClaimNextRun for a given project. Defaults to
// 30s (defaultClaimFullReconcileCooldown) when zero. Per the automation
// pipeline policy this runtime knob must be server/project TOML config.
ClaimFullReconcileCooldown time.Duration
// ClaimBroadReconcileTaskBatchLimit bounds project-wide ready/promotion work
// performed by the broad ClaimNextRun reconcile pass. Defaults to
// defaultClaimBroadReconcileTaskBatchLimit when zero. Per the automation
// pipeline policy this runtime knob must be server/project TOML config.
ClaimBroadReconcileTaskBatchLimit int
// LeaseReaperInterval is the interval at which a background goroutine
// reclaims expired-lease runs. Defaults to 60s when zero. Per the automation
// pipeline policy this runtime knob must be server/project TOML config.
LeaseReaperInterval time.Duration
// ReviewedCloseoutReconcilerInterval is the interval at which a background
// goroutine closes reviewed-open Work Tasks and releases their dependents,
// independent of the throttled ClaimNextRun full-reconcile cascade. Defaults
// to defaultReviewedCloseoutReconcilerInterval (20s) when zero. Per the
// automation pipeline policy this runtime knob must be server/project TOML
// config.
ReviewedCloseoutReconcilerInterval time.Duration
// AutomationReconcilerInterval is the interval at which a background goroutine
// runs the full claim reconcile cascade per project, independent of the
// throttled ClaimNextRun full-reconcile cascade. Without this, a starved claim
// path (slow graph storage / heavy runner churn) can leave the reconcile steps
// (ready-task queuing, plan completion, stale-claim release, review queuing)
// effectively unrun, stalling pipelines. Defaults to
// defaultAutomationReconcilerInterval (20s) when zero. Per the automation
// pipeline policy this runtime knob must be server/project TOML config.
AutomationReconcilerInterval time.Duration
// ReviewGateTimeout is the maximum duration a task may stay in
// needs_review/verifying without a review ref before the reconciler
// auto-grants a review exemption (when the task has implementation
// evidence). Default 0 = disabled. Per the automation pipeline policy this
// runtime knob must be server/project TOML config.
ReviewGateTimeout time.Duration
// MaxFindingsPerAudit bounds how many confirmed high-confidence findings a
// single area-bug-audit run will remediate. Findings beyond the cap are
// recorded as superseded (never dropped). Default applied in the service
// constructor (defaultMaxFindingsPerAudit = 10). Per-call MCP overrides are
// clamped to maxFindingsPerAuditCeiling (50). Per the automation pipeline
// policy this runtime knob must be server/project TOML config.
MaxFindingsPerAudit int
CodexBinaryPath string
Agents []AutomationAgent
PermissionResolver PermissionResolver
Governance GovernanceOptions
WorkPlanStatusTrigger WorkPlanStatusTriggerOptions
DirtyScopeRecovery DirtyScopeRecoveryOptions
}
type PermissionCheckInput ¶ added in v0.2.0
type PermissionResolver ¶ added in v0.2.0
type PermissionResolver interface {
CheckAutomationPermission(context.Context, PermissionCheckInput) (PermissionSnapshotMetadata, error)
}
type PermissionSnapshotMetadata ¶ added in v0.2.0
type ResetGitOpsRecoveryInput ¶ added in v0.3.0
type RunCondition ¶ added in v0.3.0
RunCondition is the precondition for UpdateRunIf: the conditional write only applies when the persisted run's Status and ClaimID match these values. Kept minimal (two fields) — add more only if a concrete ABA window requires it.
type RunFilter ¶
type RunFilter struct {
ProjectID string `json:"project_id,omitempty"`
AutomationID string `json:"automation_id,omitempty"`
AgentID string `json:"agent_id,omitempty"`
PlanID string `json:"plan_id,omitempty"`
Status string `json:"status,omitempty"`
OrchestratorRunID string `json:"orchestrator_run_id,omitempty"`
FailureCategory string `json:"failure_category,omitempty"`
WorkTaskStatus string `json:"work_task_status,omitempty"`
}
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
func (*Service) CallAutomationTool ¶
func (*Service) CancelAutomationsForPlan ¶ added in v0.3.0
func (svc *Service) CancelAutomationsForPlan(ctx context.Context, projectID string, planID string) error
CancelAutomationsForPlan disables all enabled automations for a plan (GAP-12). Called by CancelChain so cancelled chains don't leave stale automations that pollute the reconciler's scan. Best-effort: individual failures are skipped.
func (*Service) CancelOrphanedAutomations ¶ added in v0.3.0
func (svc *Service) CancelOrphanedAutomations(ctx context.Context, projectID string, activePlanIDs map[string]bool)
CancelOrphanedAutomations disables enabled automations whose plans belong to terminal (cancelled/completed/superseded/failed) workflow chains (GAP-12). Called from the claim path so historical orphans from chains cancelled before the CancelChain fix are cleaned up automatically.
func (*Service) CancelRun ¶ added in v0.3.0
func (svc *Service) CancelRun(ctx context.Context, input CancelRunInput) (AutomationRun, error)
func (*Service) ClaimNextRun ¶
func (svc *Service) ClaimNextRun(ctx context.Context, input ClaimNextRunInput) (ClaimedRun, error)
func (*Service) CompleteAttempt ¶
func (svc *Service) CompleteAttempt(ctx context.Context, input CompleteAttemptInput) (AutomationRun, error)
func (*Service) ComputeParallelBatch ¶
func (svc *Service) ComputeParallelBatch(ctx context.Context, input ComputeParallelBatchInput) (AutomationParallelBatch, error)
func (*Service) CreateAutomation ¶
func (svc *Service) CreateAutomation(ctx context.Context, input CreateAutomationInput) (Automation, error)
func (*Service) CreateRemediationFromFinding ¶ added in v0.2.4
func (svc *Service) CreateRemediationFromFinding(ctx context.Context, input CreateRemediationFromFindingInput) (CreateRemediationFromFindingResult, error)
func (*Service) DeleteAutomationsForPlanCascade ¶ added in v0.3.0
func (svc *Service) DeleteAutomationsForPlanCascade(ctx context.Context, projectID, planID string) (AutomationDeleteCounts, error)
DeleteAutomationsForPlanCascade permanently removes every automation owned by the given plan together with each automation's runs. Automations are flipped to a terminal status BEFORE deletion so the reconciler stops touching them. Scoped by project. Idempotent: returns zero counts and nil when no automations exist for the plan. Used by the governed chain-run delete cascade.
func (*Service) ExecuteQueuedRun ¶ added in v0.2.0
func (*Service) ForceCancelRun ¶ added in v0.3.0
func (svc *Service) ForceCancelRun(ctx context.Context, input CancelRunInput) (AutomationRun, error)
ForceCancelRun cancels an automation run including terminal (blocked/failed) runs. It is the operator recovery path: a terminal run counts toward the replacement-retry limit (countTerminalReplacementFailures) for its task, and a cancelled run with failure_category "operator_cancelled" is NOT counted (isTerminalReplacementFailure counts only failed recovery/codex/closeout failures). Force-cancelling a stuck terminal run therefore drops it from the counter, letting the orchestrator queue a fresh run after the root cause is fixed. This bypasses the normal CancelRun terminal guard deliberately — the operator attests the root cause is corrected. The audit trail records the operator-attributed cancellation via the attempt evidence ref.
func (*Service) GetAutomation ¶
func (*Service) HandleWorkPlanStatusChanged ¶ added in v0.2.1
func (svc *Service) HandleWorkPlanStatusChanged(ctx context.Context, event projectworkplan.WorkPlanStatusChange) error
func (*Service) HeartbeatRun ¶ added in v0.2.4
func (svc *Service) HeartbeatRun(ctx context.Context, input HeartbeatRunInput) (AutomationRun, error)
func (*Service) ListAutomations ¶
func (svc *Service) ListAutomations(ctx context.Context, filter AutomationFilter) ([]Automation, error)
func (*Service) ListRunsForStats ¶ added in v0.3.1
func (svc *Service) ListRunsForStats(ctx context.Context, filter RunFilter) ([]AutomationRun, error)
ListRunsForStats lists automation runs for read-only observability (the Control Tower snapshot) WITHOUT the reconcile side effects that ListRuns triggers. It mirrors ListRuns' safeRef/safeOptionalRef validation so the operator-controlled filter fields are gated identically, but it calls rawListRuns directly — zero reconcileRunningRuns / reconcileVerifyingRuns calls (B2). A stats read must never mutate run state: it would both slow the endpoint and risk a reconcile storm if a dashboard polls at high cadence.
Project scope passes a project filter; the global/all scope (Phase R1b) will call this once per configured project.
KEEP-IN-SYNC: the validation block below is a deliberate copy of ListRuns' (service.go:961). If a new RunFilter field is added and validated in ListRuns, add the same validation here — otherwise the stats read accepts a field the canonical path rejects. The only intentional difference is the absence of the two reconcile calls.
func (*Service) PurgeCancelledAutomations ¶ added in v0.3.0
func (svc *Service) PurgeCancelledAutomations(ctx context.Context, projectID, status string) (int, error)
PurgeCancelledAutomations permanently deletes all automation nodes (and their terminal runs) matching the given status for a project. This is the operator cleanup path for stale clutter that accumulates from cancelled chains and slows the reconciler's claim-next scan (KI-2 in RUN_OBSERVABILITY_GAP.md).
Only terminal statuses (cancelled/superseded/failed/disabled) may be purged. Non-terminal runs belonging to a purged automation are left intact as a safety guard. Returns the count of purged automation definitions.
func (*Service) PurgeTerminalRuns ¶ added in v0.3.0
PurgeTerminalRuns permanently deletes all automation-run nodes matching the given terminal status for a project. This is the operator cleanup path for thousands of historical completed/failed/cancelled runs that slow the reconciler's claim-next scan. Only terminal statuses may be purged.
func (*Service) ReconcileReadyAutomationsForPlan ¶ added in v0.3.0
func (*Service) ResetGitOpsRecovery ¶ added in v0.3.0
func (svc *Service) ResetGitOpsRecovery(ctx context.Context, input ResetGitOpsRecoveryInput) (AutomationRun, error)
func (*Service) RunNow ¶
func (svc *Service) RunNow(ctx context.Context, input SubmitRunInput) (AutomationRun, error)
func (*Service) SetClaimIdleSleep ¶ added in v0.3.0
func (svc *Service) SetClaimIdleSleep(sleeper func())
SetClaimIdleSleep wires the idle backoff invoked at the end of a claimGitOpsPostTaskRecovery cycle that claimed nothing (Bug #4 defense-in-depth). The server wires a bounded sleeper so the claim loop cannot re-pin the CPU re-scanning the same failed runs; tests leave it nil (no-op) and install a recorder via installClaimIdleSleepRecorder when they assert on the backoff.
func (*Service) SetWorkflowChainGitOpsRetrier ¶ added in v0.3.0
func (*Service) SetWorkflowChainPRRefRecorder ¶ added in v0.3.0
func (svc *Service) SetWorkflowChainPRRefRecorder(recorder workflowChainPRRefRecorder)
SetWorkflowChainPRRefRecorder wires the per-task -> chain-run PR ref writeback callback (projectworkflowchain.Service.RecordChainRunPullRequestRef). Best-effort: the closeout path calls it but never aborts task completion on its error. See recordTaskPullRequestRefToChain (Bug-1 fix).
func (*Service) StartAutomationReconciler ¶ added in v0.3.0
StartAutomationReconciler launches a background goroutine that periodically runs the full claim reconcile cascade per project, independent of the throttled ClaimNextRun full-reconcile cascade. This is the B2.1 starvation fix: the cascade (ready-task queuing, plan completion, stale-claim release, review queuing, gitops recovery, etc.) is no longer reachable ONLY through the claim path, so a starved/throttled claim cascade (slow graph storage or heavy runner churn) cannot leave these reconcile steps unrun. Modeled on StartReviewedCloseoutReconciler: fire-and-forget, lifecycle bound to ctx cancellation.
The tick and any concurrent claim-path reconcile cannot double-run: both acquire the same reserveClaimFullReconcile per-project lease, so when a runner-driven full reconcile is in flight the tick skips that project (and vice versa). When the tick wins the lease it runs the identical cascade (runClaimFullReconcile) with empty agentID/runnerID so the reconcile side effects fire; no run is handed to a runner from this path (there is no runner to receive it — that remains ClaimNextRun's job).
func (*Service) StartLeaseReaper ¶ added in v0.3.0
StartLeaseReaper launches a background goroutine that periodically reclaims expired-lease runs across all projects. Without this, orphaned runs (from crashed/restarted runner containers) stay stuck in running until a runner happens to poll ClaimNextRun for that project. The reaper ensures recovery even when no runner is actively polling.
func (*Service) StartReviewedCloseoutReconciler ¶ added in v0.3.0
StartReviewedCloseoutReconciler launches a background goroutine that closes reviewed-open Work Tasks and releases their dependents on a fixed cadence. This is intentionally separate from the ClaimNextRun full-reconcile cascade: that cascade is throttled (reserveClaimFullReconcile, 30s cooldown) and runs synchronously inside the runner HTTP-poll path, so under slow graph storage or heavy runner churn it can be effectively starved — leaving reviewed tasks stuck in needs_review forever and dependents never released. This tick guarantees the closeout runs regardless of claim-path pressure. Modeled on StartLeaseReaper: fire-and-forget, lifecycle bound to ctx cancellation.
func (*Service) StartZombieReviewReaper ¶ added in v0.3.1
StartZombieReviewReaper launches a background goroutine that periodically reaps zombie review runs — review automation runs stuck in a non-terminal status (queued/claiming/starting/running/verifying) whose parent implementation run is terminal or missing. These zombies suppress second-review spawns after a rework cycle because the dedup in queuePostImplementationReviewRun treats them as live duplicates (R3). Modeled on StartReviewedCloseoutReconciler: fire-and-forget, lifecycle bound to ctx cancellation, per-project isolation.
func (*Service) SubmitRun ¶
func (svc *Service) SubmitRun(ctx context.Context, input SubmitRunInput) (AutomationRun, error)
func (*Service) UpdateAutomationStatus ¶ added in v0.2.2
func (svc *Service) UpdateAutomationStatus(ctx context.Context, input UpdateAutomationStatusInput) (Automation, error)
type Store ¶
type Store interface {
CreateAutomation(context.Context, Automation) (Automation, error)
GetAutomation(context.Context, string, string) (Automation, error)
ListAutomations(context.Context, AutomationFilter) ([]Automation, error)
UpdateAutomation(context.Context, Automation) (Automation, error)
CreateRun(context.Context, AutomationRun) (AutomationRun, error)
GetRun(context.Context, string, string) (AutomationRun, error)
ListRuns(context.Context, RunFilter) ([]AutomationRun, error)
UpdateRun(context.Context, AutomationRun) (AutomationRun, error)
// ClaimRun atomically transitions a run from RunStatusQueued to the
// supplied next value (the service sets Status=RunStatusClaiming and the
// claim/runner identity before calling). It returns ErrClaimRace if the
// persisted run is no longer Queued (another claimer won), ErrNotFound if
// the run does not exist, and otherwise the written run. The read+guard+
// write happens atomically under the store's own lock so the claim
// transition is correct without an external mutex.
ClaimRun(context.Context, AutomationRun) (AutomationRun, error)
// UpdateRunIf conditionally applies next only when the persisted run's
// Status and ClaimID match the expected RunCondition. It returns
// ErrConditionMismatch if they diverged (a concurrent claim/state change
// won), ErrNotFound if the run does not exist, and otherwise the written
// run. The read+guard+write is atomic under the store's own lock so two
// concurrent guarded writes cannot both apply. Reconcile terminal-write
// guards use this to avoid clobbering a concurrently-claimed run.
UpdateRunIf(context.Context, RunCondition, AutomationRun) (AutomationRun, error)
CreateAttempt(context.Context, AutomationAttempt) (AutomationAttempt, error)
CreateParallelBatch(context.Context, AutomationParallelBatch) (AutomationParallelBatch, error)
GetParallelBatch(context.Context, string, string) (AutomationParallelBatch, error)
// PurgeAutomationsByStatus permanently deletes automation nodes matching the
// given project + status filter (e.g. "cancelled"). Used by
// PurgeCancelledAutomations to clean up stale clutter that slows the
// reconciler's claim-next scan. Returns the count of deleted nodes.
// Associated runs/attempts are cleaned via relationship cascade in the
// graph layer's DeleteNodes.
PurgeAutomationsByStatus(context.Context, string, string) (int, error)
// PurgeRunsByStatus permanently deletes automation-run nodes matching the
// given project + status (e.g. "completed"). Used to clean up thousands of
// historical terminal runs that slow the reconciler's claim-next scan.
// Only terminal statuses may be purged. Returns the count deleted.
PurgeRunsByStatus(context.Context, string, string) (int, error)
// DeleteAutomation permanently removes a single automation definition node
// by id. It does NOT remove that automation's runs (call
// DeleteRunsByAutomation first). Scoped by project; idempotent (nil when
// absent). Used by the governed chain-run delete cascade.
DeleteAutomation(context.Context, string, string) error
// DeleteRunsByAutomation permanently removes every run node owned by the
// given automation id. Scoped by project. Returns the count removed.
// Idempotent (0, nil) when none exist. Used by the governed chain-run
// delete cascade.
DeleteRunsByAutomation(context.Context, string, string) (int, error)
}
type SubmitRunInput ¶
type SubmitRunInput struct {
ProjectID string `json:"project_id,omitempty"`
AutomationID string `json:"automation_id"`
PlanID string `json:"plan_id,omitempty"`
TaskID string `json:"task_id,omitempty"`
OwnerAgent string `json:"owner_agent,omitempty"`
RunnerKind string `json:"runner_kind,omitempty"`
OrchestratorRunID string `json:"orchestrator_run_id,omitempty"`
ParentRunID string `json:"parent_run_id,omitempty"`
EvidenceRefs []string `json:"evidence_refs,omitempty"`
VerifierRefs []string `json:"verifier_result_refs,omitempty"`
SafeNextAction string `json:"safe_next_action,omitempty"`
}
type UpdateAutomationStatusInput ¶ added in v0.2.2
type WorkPlanStatusTriggerOptions ¶ added in v0.2.1
type WorkTaskAPI ¶
type WorkTaskAPI interface {
GetWorkPlan(context.Context, string, string) (projectworkplan.WorkPlan, error)
GetWorkTask(context.Context, string, string) (projectworkplan.WorkTask, error)
ListWorkTasks(context.Context, projectworkplan.WorkTaskFilter) ([]projectworkplan.WorkTask, error)
ListOpenWorkTasks(context.Context, projectworkplan.WorkTaskFilter) ([]projectworkplan.WorkTask, error)
ClaimWorkTask(context.Context, projectworkplan.WorkTaskActionInput) (projectworkplan.WorkTask, error)
StartWorkTask(context.Context, projectworkplan.WorkTaskActionInput) (projectworkplan.WorkTask, error)
AttachEvidence(context.Context, projectworkplan.AttachInput) (projectworkplan.Attachment, error)
AttachVerifierResult(context.Context, projectworkplan.AttachInput) (projectworkplan.Attachment, error)
AttachReviewResult(context.Context, projectworkplan.AttachInput) (projectworkplan.Attachment, error)
CompleteWorkTask(context.Context, projectworkplan.WorkTaskActionInput) (projectworkplan.WorkTask, error)
FailWorkTask(context.Context, projectworkplan.WorkTaskActionInput) (projectworkplan.WorkTask, error)
BlockWorkTask(context.Context, projectworkplan.WorkTaskActionInput) (projectworkplan.WorkTask, error)
// ResetWorkTaskRecovery clears a task's replacement-retry-limit block. Used by
// ForceCancelRun (FM-3) so that cancelling a terminal retry-limit run also
// unblocks the parent task atomically, instead of leaving it blocked with a
// stale TraceID and no recovery marker.
ResetWorkTaskRecovery(context.Context, projectworkplan.WorkTaskActionInput) (projectworkplan.WorkTask, error)
}