Documentation
¶
Index ¶
- func ClassifyStoredOutcome(t *pm.Task) *pm.TaskAbort
- func RanOnHost(t *pm.Task) bool
- type Abort
- type AbortClass
- type CompletionEvidence
- type ConditionFunc
- type Config
- type GateConfig
- type GateDecision
- type GateEvent
- type GateEventKind
- type Orchestrator
- func (o *Orchestrator) AddSteps(n int)
- func (o *Orchestrator) Close() error
- func (o *Orchestrator) RegisterRoute(role pm.AgentRole, prov provider.Provider)
- func (o *Orchestrator) Run(ctx context.Context) error
- func (o *Orchestrator) SetAutoEvolve(enabled bool)
- func (o *Orchestrator) SetProvider(name string)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ClassifyStoredOutcome ¶
ClassifyStoredOutcome returns the abort record a task's stored summary warrants, or nil when the summary describes work.
Exported so `cloop task audit-ledger` reports exactly what the orchestrator acts on. Two implementations of "is this summary a refusal" would drift, and a ledger command that disagrees with the loop it is auditing is worse than no command at all — which is roughly what the previous one turned out to be.
func RanOnHost ¶ added in v0.0.2
RanOnHost reports whether a task's recorded attribution says the harness ran as a process on the hub's own machine, with its filesystem and network.
Either signal alone is enough. They are written together and should always agree, but this is the predicate the UI colours a warning with, and a disagreement between two fields that claim the same thing is exactly when a warning should fire rather than resolve to "fine".
An unattributed task — both fields empty, written before Task 20244 — is not host execution. It is unknown, and saying otherwise would retroactively accuse every historical task of touching the host.
Types ¶
type Abort ¶
type Abort struct {
Class AbortClass
// Reason is operator-facing prose naming what the provider said.
Reason string
// Evidence is the matched excerpt of the agent's output, so the event
// journal records what was actually seen rather than our paraphrase.
Evidence string
// RetryAfter is when the limit lifts, when the message says so. Zero
// otherwise.
RetryAfter time.Time
}
Abort describes one aborted run.
func ClassifyAbort ¶
ClassifyAbort decides whether an agent's output is a provider or harness refusal rather than work. It never inspects a response that signalled TASK_DONE — callers must check the signal first, because a completed task is allowed to *discuss* rate limits (several in this very project do).
type AbortClass ¶
type AbortClass string
AbortClass names why a run produced no usable work.
const ( // AbortUsageLimit is a subscription or rate window the caller has // exhausted. These carry a reset time and are retryable by waiting. AbortUsageLimit AbortClass = "usage_limit" // AbortQuotaExceeded is a hard billing or organisation quota. Waiting // out the current window does not help; a human has to raise the cap. AbortQuotaExceeded AbortClass = "quota_exceeded" // AbortAuth is a credential the provider rejected. AbortAuth AbortClass = "auth_refused" // AbortHarnessRefusal is the agent harness declining to start at all. AbortHarnessRefusal AbortClass = "harness_refused" // AbortEmptyOutput is a provider that returned successfully with nothing // in it. AbortEmptyOutput AbortClass = "empty_output" // AbortZeroArtifact is a run whose persisted artifact is zero bytes. AbortZeroArtifact AbortClass = "zero_artifact" // AbortNoEvidence is a run that left no diff and no artifact behind and // never signalled TASK_DONE — nothing to show it did anything. AbortNoEvidence AbortClass = "no_evidence" )
func (AbortClass) Retryable ¶
func (c AbortClass) Retryable() bool
Retryable reports whether waiting is likely to make the next attempt succeed. Quota, auth and harness refusals need an operator, so the run should stop rather than burn the remaining tasks against the same wall.
type CompletionEvidence ¶
type CompletionEvidence struct {
// Summary is the agent's response.
Summary string
// Diff reports whether the repository changed while the task ran.
Diff bool
// ArtifactBytes is the size of the persisted artifact. For a run whose
// artifact has not been written yet this is the length of the output
// that is about to become one.
ArtifactBytes int64
}
CompletionEvidence is what a run left behind, and is the only thing that may promote an unsignalled task to done.
func (CompletionEvidence) Positive ¶
func (e CompletionEvidence) Positive() bool
Positive implements the completion rule: a task without a TASK_DONE signal reaches done only on a non-empty summary plus a non-empty diff or artifact.
type ConditionFunc ¶ added in v0.0.2
type ConditionFunc func(ctx context.Context, plan *pm.Plan, task *pm.Task) (condition.Result, error)
ConditionFunc evaluates a task's Condition field. It mirrors condition.Evaluate with the provider and workdir already bound. A non-nil error is advisory: the returned Result still decides, and condition.Evaluate deliberately returns Proceed=true when the provider call fails.
type Config ¶
type Config struct {
WorkDir string
Model string
MaxTokens int
StepTimeout time.Duration
// Inference parameter overrides (nil = use provider default).
Temperature *float64
TopP *float64
FrequencyPenalty *float64
// ExtendedThinking enables reasoning/thinking mode for supported providers.
// Anthropic: adds the "thinking" block; OpenAI o-series: sets reasoning_effort.
ExtendedThinking bool
// ThinkingBudget is the token budget for reasoning content (default 8000).
// See provider.Options.ThinkingBudget for per-provider semantics.
ThinkingBudget int
// Effort is the reasoning-effort level (low/medium/high/xhigh/max; empty =
// provider default). Non-empty overrides the effort persisted in project
// state, mirroring how Model overrides state.Model.
Effort string
Verbose bool
DryRun bool
PMMode bool
PlanOnly bool // only decompose tasks, don't execute them
RetryFailed bool // retry failed tasks in PM mode
Replan bool // force re-decompose goal (wipes existing plan, keeps history)
// MaxFailures is the number of consecutive task failures before PM mode stops (0 = default 3).
MaxFailures int
// ContextSteps is the number of recent steps to include in prompts (0 = default 3).
ContextSteps int
// StepsLimit is the maximum number of steps to run in this session only (not persisted).
// 0 means no session limit. Takes precedence over MaxSteps when both are set.
StepsLimit int
// StepDelay is the duration to wait between steps (0 = no delay).
StepDelay time.Duration
// TokenBudget is the maximum total tokens (input + output) for the session (0 = unlimited).
// When the cumulative token count reaches or exceeds this value the session pauses.
TokenBudget int
// CostLimit is the maximum estimated cost in USD for the session (0 = unlimited).
// The session warns at 80% of the limit and pauses when the limit is reached.
CostLimit float64
// Provider to use. If empty, falls back to state.Provider, then config.yaml, then claudecode.
ProviderName string
// Provider config for building providers
ProviderCfg provider.ProviderConfig
// InnovateMode enables creative/experimental feature exploration in evolve prompts.
InnovateMode bool
// Parallel enables concurrent task execution in PM mode.
// Independent tasks (all deps satisfied) run simultaneously.
Parallel bool
// MaxParallel is the maximum number of tasks to execute concurrently in PM
// parallel mode. 0 (or unset) means no limit — all ready tasks run at once.
// Setting this to 1 is equivalent to sequential execution.
MaxParallel int
// InjectContext enables project context injection (git status, file tree) into task prompts.
InjectContext bool
// AdaptiveReplan enables AI-driven replanning after task failures.
// When enabled and a task fails, the provider re-thinks the remaining work.
AdaptiveReplan bool
// ReviewMode pauses before each task and waits for human approval (y/n/skip/quit).
ReviewMode bool
// Verify enables post-task verification: after TASK_DONE, run a second AI pass to confirm
// the task was genuinely completed. If verification fails, the task is re-queued (up to 2 retries).
Verify bool
// MaxVerifyRetries is the max number of times a task can be re-queued by the verifier (default 2).
MaxVerifyRetries int
// UseMemory injects past session learnings into prompts.
UseMemory bool
// Learn extracts key learnings at end of session and saves them to memory.
Learn bool
// MemoryLimit is the max number of memory entries to inject into prompts (0 = all).
MemoryLimit int
// WebhookURL overrides the config-file webhook URL for this run (optional).
WebhookURL string
// WebhookEvents is the list of event types to fire (empty = all).
WebhookEvents []string
// WebhookSecret is used to sign each webhook POST body with HMAC-SHA256
// in the X-Hub-Signature-256 header (GitHub-style). Empty = no signing.
WebhookSecret string
// Streaming enables token-by-token output to the terminal for providers that
// support SSE streaming (anthropic, openai, ollama). When true, the orchestrator
// passes an OnToken callback to Complete(); providers that do not support
// streaming (e.g. claudecode) simply ignore it and fall back to buffered output.
Streaming bool
// Notify enables OS desktop notifications for key events: task done, task failed,
// and session complete. Uses notify-send on Linux and osascript on macOS.
Notify bool
// Hooks configures shell commands run at task and plan lifecycle events.
Hooks hooks.Config
// DiagnoseFailures enables AI-powered failure diagnosis in PM mode (sequential only).
// When a task emits TASK_FAILED, a second AI call analyzes the failure output and
// stores a diagnosis in task.FailureDiagnosis. On retry (--retry-failed), the
// diagnosis is injected into the retry prompt so the AI can correct its approach.
DiagnoseFailures bool
// GitMode enables per-task git branch workflow in PM mode (sequential only).
// Each task is executed on a dedicated branch cloop/task-<id>-<slug>.
// On TASK_DONE the branch is committed and merged back to the original branch.
// On TASK_FAILED the branch is left open for inspection.
GitMode bool
// WorktreeParallel enables per-task git worktrees in PM parallel mode.
// When true and WorkDir is a git repo, each parallel task is executed in an
// isolated worktree at .cloop/worktrees/task-<id>/ on its own branch
// (cloop/task-<id>-<slug>). On TASK_DONE the worktree's changes are
// committed and a merge request is enqueued into a serialized merge queue
// that merges branches back into the original base branch one at a time,
// avoiding conflicts between concurrent tasks. On TASK_FAILED/skipped the
// worktree is removed but the branch is kept for inspection.
WorktreeParallel bool
// ContextTokenLimit is the maximum estimated token count for step/task-result history
// included in prompts. When the accumulated history exceeds this limit the orchestrator
// prunes oldest intermediate entries (keeping the first and last two) before building
// the prompt. 0 means no limit. Default when unset: 100000.
ContextTokenLimit int
// Optimize runs the AI plan optimizer before task execution begins.
// The optimizer reviews the full task list and suggests reordering, splits,
// merges, and flags. In non-interactive mode the reordering is applied automatically;
// in interactive mode the user is prompted to approve.
Optimize bool
// OptimizeInteractive prompts the user before applying optimizer suggestions.
// When false (default), reordering is applied automatically and other suggestions
// are printed for awareness.
OptimizeInteractive bool
// Metrics is the metrics registry for this run. When non-nil the orchestrator records
// task/step/token/cost events into it. The caller is responsible for starting any HTTP
// server and writing the final JSON summary — the orchestrator writes metrics.json at
// plan completion via Metrics.WriteJSON. Pass nil to disable metrics collection.
Metrics *metrics.Metrics
// NoDedup disables semantic task deduplication in auto-evolve mode.
// By default, before injecting newly discovered tasks the orchestrator asks the
// AI to filter out candidates that duplicate existing (completed or pending) work.
// Set this to true to skip that check and inject all discovered tasks as-is.
NoDedup bool
// TagFilter restricts PM mode execution to tasks that have at least one matching tag.
// Tasks that do not match any tag in the filter are skipped for this run.
// An empty filter (default) executes all ready tasks regardless of tags.
TagFilter []string
// SlackWebhookURL is the Slack incoming webhook URL for rich notifications.
// When set, the orchestrator sends a Slack attachment on task_done, task_failed,
// and plan_complete events. Empty = disabled.
SlackWebhookURL string
// DiscordWebhookURL is the Discord webhook URL for rich notifications.
// When set, the orchestrator sends a Discord embed on task_done, task_failed,
// and plan_complete events. Empty = disabled.
DiscordWebhookURL string
// ScriptVerify enables AI-generated shell verification scripts in PM mode
// (sequential only). After each task completes with TASK_DONE the provider
// generates a 5-15 line bash script that confirms the task was accomplished
// (new files exist, commands succeed, etc.). The script and its result are
// stored as a verification artifact. If the script exits non-zero the task
// is marked failed and failure diagnosis is triggered.
ScriptVerify bool
// AutoSplit enables automatic AI-powered task splitting in PM mode (sequential only).
// When a task's FailCount reaches 2, the orchestrator asks the AI to decompose
// it into smaller subtasks that replace it in the plan. This prevents repeated
// failures on tasks that are too large or ambiguous.
AutoSplit bool
// MultiAgent enables the three-pass specialist sub-agent pipeline in PM mode
// (sequential only). Each task is processed by an architect (designs the
// approach), a coder (implements the design), and a reviewer (critiques and
// confirms). Sub-agent responses are stored as separate artifact files
// (.cloop/tasks/<id>-<slug>-multiagent/{architect,coder,reviewer}.txt).
// The reviewer's verdict overrides the coder's task signal.
MultiAgent bool
// PostReview enables automatic AI code review after each successful task in PM
// mode (sequential only). After TASK_DONE the orchestrator runs `git diff HEAD~1`,
// calls the provider for a correctness/security/style review, and stores the
// result as a task annotation with author "ai-reviewer". The verdict (PASS/FAIL)
// is surfaced in `cloop status`.
PostReview bool
// HealRetries is the maximum number of auto-heal re-attempts after a TASK_FAILED
// signal in PM sequential mode. On each attempt the orchestrator diagnoses the
// failure, builds a mutated retry prompt incorporating the root cause and fix
// strategy, and re-executes the task. 0 means use the default (2). When NoHeal
// is true this field is ignored entirely.
HealRetries int
// NoHeal disables the auto-heal loop. When true, TASK_FAILED immediately
// proceeds to permanent failure handling without any re-attempt.
NoHeal bool
// LogJSON switches all structured event output to newline-delimited JSON (NDJSON).
// When true, key lifecycle events (session_start, task_start, task_done, task_failed,
// task_skipped, step, heal, session_done) are emitted as JSON objects to stdout.
// Decorative color/text output is suppressed so the stream is machine-parseable.
// Equivalent to the --log-json CLI flag.
LogJSON bool
// RiskCheck enables pre-execution AI risk assessment for each task in PM mode
// (sequential only). Before a task begins executing the orchestrator calls the
// risk package to assess findings. Tasks with at least one CRITICAL finding are
// aborted (marked failed) unless RiskForce is also set.
RiskCheck bool
// RiskForce overrides CRITICAL risk findings when RiskCheck is enabled. When
// true, CRITICAL tasks are executed anyway with a prominent warning instead of
// being aborted. Has no effect when RiskCheck is false.
RiskForce bool
// ConsensusN, when > 0, enables multi-model consensus for critical tasks
// (priority P0/P1 or tagged "critical"). The task prompt is fanned out to up
// to N configured providers in parallel; an AI judge then scores each response
// on correctness, safety, and completeness, and the highest-scoring response
// is used. The judge call uses the primary provider. The consensus decision
// and runner-up scores are appended to the task artifact.
ConsensusN int
// NoCodeContextInject disables automatic codebase context snippet injection
// in PM mode task prompts. When false (default), CollectRelevantContext scans
// the working directory for files matching the task keywords and prepends up
// to ~2000 tokens of relevant snippets to each task prompt. Set this to true
// (via --no-context-inject) to disable the feature entirely.
NoCodeContextInject bool
// RequireApproval enables the human-in-the-loop approval gate for P0/P1 tasks
// in PM sequential mode. When true, any task with priority <= 1 OR
// RequiresApproval:true is paused before execution and the user is prompted
// with [y/n/skip/edit]. Pre-approved tasks (task.Approved:true set via
// 'cloop task approve') bypass the interactive prompt automatically.
RequireApproval bool
// SkipClarify disables the interactive goal clarification Q&A dialog that
// normally runs before pm.Decompose() when stdin is a TTY. Set this to true
// for automation, CI, or when the goal is already fully specified.
SkipClarify bool
// AutoEval enables automatic AI quality scoring after each successful task
// in PM sequential mode. After TASK_DONE the orchestrator scores the task
// output against the default rubric and saves the result to
// .cloop/evals/<task-id>.json. The weighted average is printed to the terminal.
AutoEval bool
// Budget configures daily token and USD spend limits. When set, the
// orchestrator checks the budget before each task and aborts with a clear
// message when any limit is exceeded. Threshold alerts are fired via
// desktop/webhook notifications when AlertThresholdPct is crossed.
Budget config.BudgetConfig
// NotifyCfg holds notification channel settings used by the budget enforcer
// to send threshold alerts. It mirrors config.NotifyConfig.
NotifyCfg config.NotifyConfig
// ClaudeCode holds claudecode-specific configuration including per-project
// caps on the global Anthropic subscription utilization. When MaxWeeklyPct
// (or any of the other per-window caps) is > 0, the orchestrator queries
// the Anthropic OAuth usage API before each task and aborts when the cap
// has been reached. Only enforced when the active provider is "claudecode".
ClaudeCode config.ClaudeCodeConfig
// DocsUpdateOnComplete runs `cloop docs update --yes` after the plan
// finishes. When true, all tracked documentation files are AI-refreshed
// automatically at the end of a successful PM run.
DocsUpdateOnComplete bool
// DocsUpdateFile limits the post-plan docs update to a single file.
// Empty means all tracked docs files are updated.
DocsUpdateFile string
// CalibrationFactor scales AI-generated time estimates in new plans.
// Set by 'cloop task effort-calibrate --apply'. 0 and 1.0 are equivalent (no scaling).
// Values > 1.0 inflate estimates (AI historically underestimates), < 1.0 deflate.
CalibrationFactor float64
// TracingEnabled wraps the provider with an OTel tracing decorator when true.
// Spans are exported to the endpoint configured in config.yaml under the
// "tracing" key. When false (default), no tracing overhead is incurred.
TracingEnabled bool
// AutoPromote enables deadline-aware automatic priority escalation at the
// start of each task-selection cycle in PM sequential mode.
// For each pending/in-progress task whose deadline is within
// AutoPromoteThresholdDays days, the priority is escalated by 1.
// Tasks that are direct prerequisites of overdue tasks are also promoted.
AutoPromote bool
// AutoPromoteThresholdDays is the number of days remaining before the
// deadline at which auto-promotion kicks in (default 3).
AutoPromoteThresholdDays int
// CoachMode runs a pre-task AI coaching session before each task in PM
// sequential mode. The AI plays the role of a senior engineer and gives
// 3-5 concrete, actionable tips specific to the task: how to approach it
// well, what to watch out for, and what done looks like.
CoachMode bool
// TaskTimeoutMinutes is the process-wide default per-task wall-clock
// budget applied when neither Task.MaxMinutes nor state.DefaultMaxMinutes
// is set. Zero means "no task timeout" (Task 20148): by default tasks run
// without any wall-clock deadline. A positive value is an explicit opt-in,
// validated to [OrchestratorTaskTimeoutMinutesLower, ...Upper]. Sourced
// from config.Orchestrator.TaskTimeoutMinutes by cmd/run.go.
TaskTimeoutMinutes int
}
type GateConfig ¶ added in v0.0.2
type GateConfig struct {
// TagFilter, when non-empty, restricts execution to tasks carrying at
// least one of these tags. Non-matching tasks are skipped, not deferred.
TagFilter []string
// Parallel selects batch semantics. When false the gate considers only the
// single highest-priority eligible task, matching the sequential loop.
Parallel bool
// EvalCondition evaluates task conditions. When nil, a task carrying a
// condition proceeds and the gate emits a GateConditionError so the gap is
// visible rather than silent.
EvalCondition ConditionFunc
}
GateConfig is the slice of run configuration that decides what runs.
type GateDecision ¶ added in v0.0.2
type GateDecision struct {
// Runnable holds the tasks that cleared every gate, in the order the
// caller should launch them. Empty means nothing runs this iteration.
Runnable []*pm.Task
// Events records every skip and observation, in the order they occurred.
Events []GateEvent
// Exhausted is true when no pending task had its dependencies satisfied
// and the blocked sweep found nothing either — the plan has no more work
// the gate can unblock, so the caller should leave its loop.
Exhausted bool
}
GateDecision is the result of one gating pass over a plan.
func GateTasks ¶ added in v0.0.2
func GateTasks(ctx context.Context, plan *pm.Plan, cfg GateConfig) GateDecision
GateTasks decides what runs next. It applies, in order:
- Eligibility — pending tasks whose dependencies are all done or skipped.
- The blocked sweep — only when nothing is eligible, so a task whose failed dependency is still being healed is not skipped prematurely.
- Selection — every eligible task in parallel mode, otherwise the single highest-priority one.
- The tag filter.
- The condition gate.
Skips are applied to the plan (status plus annotation) before returning; the caller is responsible for printing Events and persisting the plan when Skipped() is non-zero.
func (GateDecision) Skipped ¶ added in v0.0.2
func (d GateDecision) Skipped() int
Skipped returns the number of tasks this pass moved to skipped. A non-zero count means the plan was mutated and the caller must persist it.
func (GateDecision) SkippedIDs ¶ added in v0.0.2
func (d GateDecision) SkippedIDs() []int
SkippedIDs returns the IDs of the tasks this pass skipped, in order.
type GateEvent ¶ added in v0.0.2
type GateEvent struct {
Kind GateEventKind
Task *pm.Task
// Line is the console message, already formatted and newline-terminated.
Line string
// Annotation is the text recorded on the task. Empty for non-skip events.
Annotation string
}
GateEvent is one decision the gate made about one task, carrying both the console line and — for skips — the plan annotation. Keeping the two together is what stops the wording of a skip diverging between callers.
type GateEventKind ¶ added in v0.0.2
type GateEventKind string
GateEventKind classifies one line of the gate's reasoning. Skip kinds change a task's status; the rest are observations worth logging.
const ( // GateSkipBlocked marks a pending task that can never run because a // dependency failed or timed out. GateSkipBlocked GateEventKind = "skip_blocked" // GateSkipTagFilter marks a task cut by the active --tag filter. GateSkipTagFilter GateEventKind = "skip_tag_filter" // GateSkipCondition marks a task whose condition gate refused to proceed. GateSkipCondition GateEventKind = "skip_condition" // GateConditionMet records a condition that passed. GateConditionMet GateEventKind = "condition_met" // GateConditionError records an evaluator failure. The task still runs: // a transient provider outage must not silently skip work. GateConditionError GateEventKind = "condition_error" )
type Orchestrator ¶
type Orchestrator struct {
// contains filtered or unexported fields
}
func (*Orchestrator) AddSteps ¶
func (o *Orchestrator) AddSteps(n int)
func (*Orchestrator) Close ¶
func (o *Orchestrator) Close() error
Close releases resources held by the orchestrator. Safe to call multiple times. Any queue entries still in "running" are marked failed (interrupted) before the database is closed.
func (*Orchestrator) RegisterRoute ¶
func (o *Orchestrator) RegisterRoute(role pm.AgentRole, prov provider.Provider)
RegisterRoute adds a role→provider binding to the orchestrator's router. Must be called before Run().
func (*Orchestrator) SetAutoEvolve ¶
func (o *Orchestrator) SetAutoEvolve(enabled bool)
func (*Orchestrator) SetProvider ¶
func (o *Orchestrator) SetProvider(name string)
SetProvider persists the provider name in state so subsequent runs default to the same provider.