Documentation
¶
Overview ¶
Package engine enforces the mechanics so the foreman can stick to judgment: it is the mailbox every message passes through, it turns ready tasks into jobs, applies the routine rules (fix rounds, capped retries, dependency readiness, account cooling), dispatches each job to the best eligible agent by measured outcomes, and wakes the foreman only for judgment events. It also owns the engine's lifecycle: a single instance locked on its store, started on demand and shut down when idle.
Index ¶
- Constants
- Variables
- func Canonicalize(rawURL string) (string, error)
- func CheckBaseFetchable(ref string) error
- func ConfigHash(content []byte) string
- func ConfigHashWith(content []byte, instructions *string) string
- func DefaultRoleFile(role string) string
- func ListSnapshots(dir string) ([]string, error)
- func LookupProjectTx(tx *sql.Tx, raw string) (int64, error)
- func ProjectConfigDefaults(content []byte, repo, check, tools string) ([]byte, error)
- func ProjectConfigGuidance(content []byte) ([]byte, error)
- func ProjectConfigRoleKey(content []byte, role string) ([]byte, error)
- func RedactRepoURL(raw string) string
- func Score(attempts, successes int) float64
- func SnapshotPath(dir string, t time.Time) string
- func TaskCapsLabel(caps map[string]int) string
- func ValidReviewFamily(word string) bool
- func ValidReviewTier(word string) bool
- func ValidateInstructions(text string) error
- func ValidateRoleText(role, text string) error
- func ValidateSettingValue(key, value string) error
- func ValidateSkills(skills []Skill) error
- type AgentAdvert
- type AmendPlanResult
- type Answer
- type AnswerResult
- type ArtifactInfo
- type AskResult
- type AttachResult
- type CanonReport
- type CapacityAgent
- type CapacityHeadroom
- type CapacityLayer
- type CapacitySnapshot
- type Completion
- type Config
- type ConfigChange
- type CoolingAgent
- type CreatePlanResult
- type CreatedTask
- type Engine
- func (e *Engine) AcceptPlan(ctx context.Context, project, plan int64, reason string) (PlanDecision, error)
- func (e *Engine) AmendPlan(ctx context.Context, project, plan int64, am PlanAmendment) (AmendPlanResult, error)
- func (e *Engine) Answer(ctx context.Context, project, taskID int64, text string) (AnswerResult, error)
- func (e *Engine) ApprovePlan(ctx context.Context, project, plan int64) (PlanDecision, error)
- func (e *Engine) ApprovePlanVersion(ctx context.Context, project, plan, version int64, stopAsking bool) (PlanDecision, error)
- func (e *Engine) ArchiveProject(ctx context.Context, repoURL string, cancel bool) (Answer, error)
- func (e *Engine) Artifact(ctx context.Context, id int64) (*ArtifactInfo, *os.File, error)
- func (e *Engine) AskForeman(ctx context.Context, repoURL, text, source string) (AskResult, error)
- func (e *Engine) AskUser(ctx context.Context, project int64, text string) (int64, error)
- func (e *Engine) Attach(ctx context.Context, project int64, force bool) (AttachResult, error)
- func (e *Engine) AttachHeartbeat(ctx context.Context, holdID int64) (Answer, time.Time, error)
- func (e *Engine) BumpFenceEpoch(ctx context.Context) (int, error)
- func (e *Engine) Cancel(ctx context.Context, jobID int64) (Answer, error)
- func (e *Engine) CanonicalizeProjects(ctx context.Context) (CanonReport, error)
- func (e *Engine) Capacity(ctx context.Context, projectID int64) (CapacitySnapshot, error)
- func (e *Engine) CapsUsage(ctx context.Context) (caps.Usage, error)
- func (e *Engine) Complete(ctx context.Context, owner, jobID int64, leaseID string, c Completion) (Answer, error)
- func (e *Engine) CreatePlan(ctx context.Context, project int64, title string, tasks []PlanTask) (CreatePlanResult, error)
- func (e *Engine) DueProbes(ctx context.Context, runner int64) ([]Probe, error)
- func (e *Engine) ForemanEvents(ctx context.Context, project int64) (ForemanInbox, error)
- func (e *Engine) ForemanSlot(ctx context.Context, project int64) (Slot, error)
- func (e *Engine) Heartbeat(ctx context.Context, owner, jobID int64, leaseID string, prog Progress) (Answer, time.Time, error)
- func (e *Engine) History(ctx context.Context, q HistoryQuery) (history.Page, error)
- func (e *Engine) HoldTokenScope(ctx context.Context, secret string) (project, hold int64, ok bool, err error)
- func (e *Engine) ImportProject(ctx context.Context, repoURL string, content []byte, instructions *string, ...) (ImportResult, error)
- func (e *Engine) ImportProjectConfig(ctx context.Context, repoURL string, content []byte, accept bool) (ImportResult, error)
- func (e *Engine) ImportProjectConfigWith(ctx context.Context, repoURL string, content []byte, instructions *string, ...) (ImportResult, error)
- func (e *Engine) ImportRoleFile(ctx context.Context, repoURL, role, text string) (*RoleInstruction, error)
- func (e *Engine) ImportSkills(ctx context.Context, repoURL string, skills []Skill) ([]Skill, error)
- func (e *Engine) Job(ctx context.Context, id int64) (*JobInfo, error)
- func (e *Engine) LandApprove(ctx context.Context, project, taskID int64, stopAsking bool) (LandDecision, error)
- func (e *Engine) LandReject(ctx context.Context, project, taskID int64) (LandDecision, error)
- func (e *Engine) Lease(ctx context.Context, owner int64, harnesses, labels []string) (*LeaseGrant, error)
- func (e *Engine) LeaseTTL() time.Duration
- func (e *Engine) LeaseTokenScope(ctx context.Context, secret string) (project, job int64, ok bool, err error)
- func (e *Engine) LiveAttaches(ctx context.Context) (int, error)
- func (e *Engine) LiveJobs(ctx context.Context) (int, error)
- func (e *Engine) MarkDraining(ctx context.Context, runner int64) error
- func (e *Engine) MarkRecovered(ctx context.Context) (epoch int, err error)
- func (e *Engine) MaxArtifactBytes() int64
- func (e *Engine) PlanWorkerSeconds(ctx context.Context, planID int64) (time.Duration, error)
- func (e *Engine) ProjectForeman(ctx context.Context, repoURL string) (*ForemanSetting, error)
- func (e *Engine) ProjectSettings(ctx context.Context, repoURL string) (*ProjectSettings, error)
- func (e *Engine) Projects(ctx context.Context) ([]ProjectInfo, error)
- func (e *Engine) Prune(ctx context.Context, before time.Time) (PruneReport, error)
- func (e *Engine) PushApprove(ctx context.Context, repoURL string) (PushApproveResult, error)
- func (e *Engine) ReadIssue(ctx context.Context, projectID int64, number int, comments bool) (Issue, error)
- func (e *Engine) RecordInstance(ctx context.Context, pid int64, started time.Time) error
- func (e *Engine) RecordLanded(ctx context.Context, repoURL, branch string, landed []LandedCommit) error
- func (e *Engine) RecoverForemanWakes(ctx context.Context) error
- func (e *Engine) RecoverPlans(ctx context.Context, extraRepos []string) (results []PlanRecovery, warnings []string, err error)
- func (e *Engine) Register(ctx context.Context, name string, maxParallel int, agents []AgentAdvert) (int64, error)
- func (e *Engine) RegisterStarted(ctx context.Context, name string, startedAt time.Time, maxParallel int, ...) (int64, error)
- func (e *Engine) RejectPlan(ctx context.Context, project, plan int64) (PlanDecision, error)
- func (e *Engine) RejectPlanVersion(ctx context.Context, project, plan, version int64) (PlanDecision, error)
- func (e *Engine) Release(ctx context.Context, owner, jobID int64, leaseID, reason string) (Answer, error)
- func (e *Engine) ReleaseAttach(ctx context.Context, holdID int64, sessionID string) (Answer, error)
- func (e *Engine) ReopenStrandedPlans(ctx context.Context) ([]int64, error)
- func (e *Engine) ReportProbe(ctx context.Context, runner, agent int64, ok bool) error
- func (e *Engine) ResetAgents(ctx context.Context, harness, model string) (int, error)
- func (e *Engine) RestartTask(ctx context.Context, project, taskID int64, note string) (RestartResult, error)
- func (e *Engine) Retire(ctx context.Context, project int64, tasks []int64, reason, caller string) (RetireResult, error)
- func (e *Engine) RetryLanding(ctx context.Context, project, taskID int64) (LandRetryResult, error)
- func (e *Engine) ReviewBranch(ctx context.Context, repoURL, branch string) (ReviewBranchResult, error)
- func (e *Engine) RoleInstruction(ctx context.Context, repoURL, role string) (*RoleInstruction, error)
- func (e *Engine) RunnerID(ctx context.Context, name string) (int64, error)
- func (e *Engine) SetProjectForeman(ctx context.Context, repoURL, harness, model string) (ForemanSetting, error)
- func (e *Engine) SetProjectForemanEffort(ctx context.Context, repoURL, harness, model, effort string) (ForemanSetting, error)
- func (e *Engine) SetProjectSettings(ctx context.Context, repoURL string, values map[string]string) (ProjectSettings, error)
- func (e *Engine) SetRoleInstruction(ctx context.Context, repoURL, role, text string) (*RoleInstruction, error)
- func (e *Engine) SetWakeBudget(ctx context.Context, repoURL string, wakes int) (WakeBudgetInfo, error)
- func (e *Engine) Skills(ctx context.Context, repoURL string) ([]Skill, error)
- func (e *Engine) SnapshotIfDue(ctx context.Context) (string, error)
- func (e *Engine) Stats(ctx context.Context, project int64) ([]ProjectStats, error)
- func (e *Engine) Status(ctx context.Context, project int64) (StatusInfo, error)
- func (e *Engine) StatusAll(ctx context.Context, project int64, withText bool) (StatusInfo, error)
- func (e *Engine) StatusWithInstructions(ctx context.Context, project int64) (StatusInfo, error)
- func (e *Engine) Submit(ctx context.Context, p SubmitParams) (SubmitResult, error)
- func (e *Engine) SubmitForemanTurn(ctx context.Context, project int64, events []string) (SubmitResult, error)
- func (e *Engine) SubmitTask(ctx context.Context, p TaskParams) (TaskResult, error)
- func (e *Engine) Sweep(ctx context.Context) (SweepReport, error)
- func (e *Engine) SyncPlanFiles()
- func (e *Engine) Task(ctx context.Context, id int64) (*TaskInfo, error)
- func (e *Engine) TaskLand(ctx context.Context, id int64) (*TaskLandInfo, error)
- func (e *Engine) UnimportRoleFiles(ctx context.Context, repoURL string, configured map[string]bool) ([]RoleFileRemoval, error)
- func (e *Engine) UploadArtifact(ctx context.Context, owner, jobID int64, leaseID, kind string, data []byte) (Answer, *ArtifactInfo, error)
- func (e *Engine) WaitPlanFiles()
- func (e *Engine) WakeBudget(ctx context.Context, repoURL string) (*WakeBudgetInfo, error)
- type ExtraRepo
- type ExtraReposSpec
- type ForemanInbox
- type ForemanSetting
- type HeldJob
- type HeldTask
- type HistoryQuery
- type ImportResult
- type InfraRetry
- type Issue
- type IssueComment
- type JobInfo
- type Kind
- type LandDecision
- type LandMessage
- type LandRetryResult
- type LandedCommit
- type LeaseGrant
- type Mode
- type NeedsYouItem
- type PlanAmendment
- type PlanDecision
- type PlanRecovery
- type PlanRespec
- type PlanRetarget
- type PlanRetier
- type PlanTask
- type Probe
- type Progress
- type ProjectConfig
- type ProjectInfo
- type ProjectSettings
- type ProjectStats
- type PruneReport
- type PushApproveResult
- type Ranked
- type RestartResult
- type RetireResult
- type RetiredPlan
- type ReviewBranchResult
- type ReviewConfig
- type RoleFileRemoval
- type RoleInstruction
- type RoleSnapshot
- type RoundCaps
- type SettingValue
- type SetupSpec
- type Skill
- type SkillFileDigest
- type Slot
- type SlotState
- type StatsRow
- type StatusInfo
- type StatusInstructions
- type StatusJob
- type StatusPlan
- type StatusReport
- type StatusRoleInstructions
- type StatusSkill
- type StatusTask
- type SubmitParams
- type SubmitResult
- type SweepReport
- type TaskInfo
- type TaskInstructions
- type TaskLandInfo
- type TaskParams
- type TaskResult
- type TaskSkill
- type WakeBudgetInfo
- type WakeOutcome
Constants ¶
const ( ArtifactTranscript = "transcript" ArtifactCheckLog = "check_log" // ArtifactSetupLog is the log of the setup command (§5.7), kept next // to the check log so a failed setup can be read. ArtifactSetupLog = "setup_log" )
The artifact kinds the engine stores (§9): the session transcript a run's events make, and the full log of a check the runner ran (§5.5). The set lives in the schema's CHECK; these are the words the engine itself writes.
const ( OutcomeStalled = "stalled" OutcomeTimeCap = "time_cap" )
The runner's stop outcomes (§5.6): the outcome a completion carries when the runner ended the run itself — the worker stalled, or the run passed its class time cap. Both end the job as stalled; the result block tells the two apart, and the foreman that always takes a stalled job (§5.6) reads it there.
const ( OutcomeLandHeld = "held" OutcomeLandNotReady = "not ready" )
The land_ready gate's outcomes (issue #87): the answers of the repo-relative command a land job runs before landing. held is the command's own hold — exit 1, its first output line the reason — and nothing lands until the user releases it; not ready is every other way it could not say yes — exit 2, another exit, a spawn failure, a timeout — and the landing is requeued on the not-before backoff. Both ride with a Result — the gate's record — never an Error, and end the job done: they are the gate's answers, not failures.
const ( ExtraSourceDefault = "project-default" ExtraSourceTask = "task" )
The sources of a recorded list (§5.8 "Resolution").
const ( LandRetryQueued = "queued" // the land job is queued; JobID names it LandRetryHeld = "held" // the land gate held the task; Reason names the rule LandRetryStaleBase = "stale_base" // the landing was refused on a stale base; the task parked again LandRetryRefused = "refused" // nothing changed; Reason says why )
The outcomes of a land retry, LandRetryResult.Outcome.
const ( RespecReplace = "replace" RespecAppend = "append" )
Spec edit modes of a PlanRespec.
const ( PlanRebuilt = "rebuilt" // the plan was absent from the store; built from its file PlanUpdated = "updated" // the file was newer than the plan the store held PlanKept = "kept" // the store's plan is as new as its file, or newer PlanSkipped = "skipped" // the file could not be applied; Detail says why )
Plan recovery's per-plan outcomes.
const ( ReviewTierBelow = "below" ReviewTierSame = "same" )
The review.tier settings (§7, §8, decided 2026-10-20): a review asks for the tier below its task's effective tier, or for the same tier.
const ( ReviewFamilyPrefer = "prefer" ReviewFamilyRequire = "require" )
The review.family settings (§7, §8, issue #126): require keeps the review placement cross-family-only; prefer, the default, makes another family a preference — the author's own family and then the author's own agent serve when no agent of another family is available.
const ( RoleForeman = "foreman" RolePlanReview = "plan_review" RoleWorker = "worker" RoleReviewer = "reviewer" )
The roles a project can set an instruction text for.
const ( RoleStateNotConfigured = "not configured" RoleStateEmptyByChoice = "empty by choice" RoleStateFile = "file" )
The three states of a role text (§5.9): not configured (no row: no text applies), empty by choice (a row whose local text is empty: no text applies), and file (a row holding a text).
const ( SettingApprovalMaxTasks = "approval_max_tasks" SettingApprovalBudget = "approval_budget" SettingApprovalClasses = "approval_classes" SettingApprovalProtectedPaths = "approval_protected_paths" // The gates and land rules (§6.3). gate_plan is enforced today: ask // holds every plan at the plan-approval gate. The land rules and // gates are read by the land job (§7.1). SettingGatePlan = "gate_plan" SettingGateLand = "gate_land" SettingGatePush = "gate_push" // The plan review (§6.2): plan_review turns it on and off // (default on), plan_review.tier and plan_review.effort set the // reviewer's tier (default standard) and effort (default medium). // Local only — the shared project.yaml's keys cannot carry a dot. SettingPlanReview = "plan_review" SettingPlanReviewTier = "plan_review.tier" SettingPlanReviewEffort = "plan_review.effort" SettingApprovalTiers = "approval_tiers" SettingApprovalRestarted = "approval_restarted" SettingLandTarget = "land_target" SettingAfterLand = "after_land" SettingLandReady = "land_ready" SettingDefaultCheck = "default_check" SettingWorkerTools = "worker_tools" // The environment step (§5.7): the command, the repo-relative files // whose contents key its cache, and its time bound in minutes. Schema // 1 of the shared file is flat, so the three are sibling keys. SettingSetup = "setup" SettingSetupCacheKeyFiles = "setup_cache_key_files" SettingSetupTimeout = "setup_timeout_minutes" // The project's daily foreman wake budget (§4.3): headless foreman // turns per UTC day. Local only — the shared project.yaml refuses // it — and set through `brigada budget --wakes N`, which also // re-evaluates the waiting wake. SettingForemanWakesPerDay = "foreman_wakes_per_day" // The foreman session's rotation threshold (§4.2): when the last // completed turn of the current session generation read more input // tokens (input plus cached) than this, the next turn or attach starts // a fresh session. 0 never rotates. SettingForemanRotateTokens = "foreman_rotate_tokens" // The project's instruction text for every worker and reviewer // (§5.9). Stored as the text itself: the client reads the file named // by project.yaml's instructions_file and sends the content. SettingInstructions = "instructions" // The plan-file mirror (§6.4): on (default) pushes each plan's file to // the repo's remote; off writes and pushes nothing. SettingPlanFiles = "plan_files" )
The project settings the user can edit (§6.3 "Configurable per project"). One read/write path serves the CLI and the approval limit: SetProjectSettings validates a value with the parser below and stores it in the shape approvalSettings reads, so a value the CLI accepts is one the limit understands.
const ( PlanFilesOn = "on" PlanFilesOff = "off" )
The values plan_files takes.
const ( GateAuto = "auto" GateAsk = "ask" GateNever = "never" )
The values a gate takes (§6.3): auto runs on, ask waits for the user; the push gate may also be never, which lands locally.
const ( SourceDefault = "default" SourceLocal = "local" SourceImported = "imported" )
Where a setting's value comes from (§13.1 precedence): a local override the user set, the last imported project.yaml, or the built-in default.
const ( MaxSkillBytes = 4096 MaxWorkerPromptBytes = 16384 MaxReviewerPromptBytes = 24576 )
The bounds of §5.9 and §5.10, in bytes of UTF-8: one skill, and what a worker and a reviewer prompt may carry of instructions plus skills (one role text and two skills at their maxima for a worker, two role texts and two skills for a reviewer). Above a bound the import or the task is refused, never truncated.
const ( SkillForWorker = "worker" SkillForReviewer = "reviewer" SkillForBoth = "both" )
The audiences a skill's 'for:' frontmatter names (§5.10): a worker skill is omitted from the reviewer's text, a reviewer skill from the worker prompt. Both is the default.
const DefaultSkillsDir = ".brigada/skills"
DefaultSkillsDir is skills_dir's path when the key is written empty.
const ErrorKindInfra = "infra"
ErrorKindInfra sorts a completion's Error as an infrastructure failure of the job's own plumbing (§3's transient error, the class a failed setup holds, §5.7): the runner could not fetch the repository's remote — the network not up yet in the seconds after a wake, a host that will not resolve, an outage of hours — or its CLI could not be built, started or connected. The engine retries the job on the infra backoff (§5.1), and neither the agent nor a round is charged.
const ErrorKindSetup = "setup"
ErrorKindSetup sorts a completion's Error as a setup failure (§5.7): an infrastructure failure of the job, neither a failed check nor a worker failure.
const ExtraRepoEnvPrefix = "BRIGADA_REPO_"
ExtraRepoEnvPrefix is reserved for the variables that name the checkouts, so none can clash with a variable the runner sets itself (§5.8).
const MaxForemanWakesPerDay = 1000
MaxForemanWakesPerDay bounds a set wake budget (§4.3): a sane ceiling on the day's foreman turns, so a typo cannot buy a thousand-turn day.
const MaxInstructionsBytes = 8192
MaxInstructionsBytes bounds the project instruction text (§5.9): a starting value, open in §16 item 11. Above it a set or an import is refused, never truncated.
const MaxRoleInstructionBytes = MaxInstructionsBytes
MaxRoleInstructionBytes bounds one role's text: the same starting value as the worker setting's (§5.9), refused above, never truncated.
const ProjectConfigPath = ".brigada/project.yaml"
ProjectConfigPath is where the file lives, relative to the repo root.
const ProjectConfigSchema = 1
ProjectConfigSchema is the one schema version this build reads.
const SettingExtraRepos = "extra_repos"
SettingExtraRepos is the project default list (§5.8, §13.1). Schema 1 of the shared file is flat, so an entry is one string, `name url [ref]`, the parts separated by white space (a URL and a git ref hold none).
const SourceGenerated = "generated"
SourceGenerated is the source of a text seeded from the embedded defaults — by the role_instructions migration, or a later seeding — with the defaults version in the settings-version column.
const TimeLayout = "2006-01-02T15:04:05.000000000Z07:00"
TimeLayout is the store's timestamp format (§11): fixed-width UTC text, so lexicographic order is chronological order and the schema's TEXT columns can be compared and indexed as they are written. Tests and tools that seed store timestamps directly format with it.
const UserMessageLimit = 16 << 10
UserMessageLimit bounds one user message (§4.2 item 3): a request for the foreman, a few paragraphs at most, never a file pasted whole.
Variables ¶
var ( ErrNoPlan = errors.New("no such plan") ErrPlanNotAwaiting = errors.New("plan is not awaiting approval") // ErrPlanNotParked refuses a plan_accept on a plan not parked at // needs_foreman by a changes verdict (§6.2). ErrPlanNotParked = errors.New("plan is not parked at needs_foreman") // ErrPlanVersionStale marks a decision that names a plan version the // plan has moved past (§4.6): an amendment landed after the // user looked, so nothing is applied and the plan must be shown again. ErrPlanVersionStale = errors.New("plan changed since it was shown") )
Refusals the plan decisions give the caller.
var ErrArtifactTooLarge = errors.New("artifact over the size cap")
ErrArtifactTooLarge is the upload cap speaking (§9): one blob over MaxArtifactBytes. The API answers it with the caller's 413.
var ErrInvalidAgent = errors.New("invalid agent advertisement")
ErrInvalidAgent marks a refusal the caller caused: an advertisement the engine will not take, whatever its reason. The API turns it into the caller's 400; every other Register error is the engine's own and surfaces as an internal failure.
var ErrInvalidArtifact = errors.New("invalid artifact")
ErrInvalidArtifact marks a refusal the caller caused: an artifact the engine will not take, whatever its reason. The API turns it into the caller's 400; every other upload error is the engine's own.
var ErrInvalidForeman = errors.New("invalid foreman setting")
ErrInvalidForeman marks a refusal the caller caused: a foreman setting the engine will not take, whatever its reason — an empty field, a harness that cannot resume headless. The API turns it into the caller's 400; every other SetProjectForeman error is the engine's own and surfaces as an internal failure.
var ErrInvalidMessage = errors.New("invalid user message")
ErrInvalidMessage marks a user message the engine will not take: an empty one, or one past the size bound.
var ErrInvalidRepoURL = errors.New("invalid repo URL")
ErrInvalidRepoURL marks a repo URL the engine will not take: empty, malformed, or ambiguous in a way the string alone cannot settle (design §13.1 item 1). The message names the problem and never quotes the URL, which may carry a credential.
var ErrInvalidReview = errors.New("invalid review")
ErrInvalidReview marks a standalone review the engine will not take: one that names no branch.
var ErrInvalidSetting = errors.New("invalid project setting")
ErrInvalidSetting marks a refusal the caller caused: an unknown key or a value the key does not take. The API turns it into a 400.
var ErrIssueNotFound = errors.New("no such issue or pull request on the project's repository")
ErrIssueNotFound is the number that does not exist on the project's own repository (§12.2): its own reason, for the API answers it not-found while the rest answer the caller's mistake.
var ErrIssueRead = errors.New("issue unreadable")
ErrIssueRead marks every reason an issue could not be read that the foreman can report and the user can act on (§12.2): the repository is not on GitHub, gh is missing or not logged in, the project is gone. The API answers it as the caller's refusal with the reason in its message; the tool never falls back to searching anywhere else.
var ErrNoAgent = errors.New("no such agent")
ErrNoAgent is the reset's refusal when no registered agent matches the harness and model named.
var ErrNoProject = errors.New("no such project")
ErrNoProject marks a user message that names no project the engine knows: a repo URL no project has, or none named while the engine holds no project, or several.
var ErrNoReviewer = errors.New("no eligible reviewer")
ErrNoReviewer marks a standalone review nobody can take (§7): no agent is ready, out of cooling and offering the review class — and, under review.family=require (issue #126), of a family other than the author's — or the author's family cannot be told and no default reviewer is configured, which require alone refuses.
var ErrPlanNotLive = errors.New("plan is not live")
ErrPlanNotLive refuses an amendment of a plan that is done or cancelled.
var ErrProjectArchived = errors.New("project was removed")
ErrProjectArchived marks a repository removed from the engine. Its stored identity and history remain available, and registration restores that row.
var ErrProjectBusy = errors.New("project has live jobs or active plans")
ErrProjectBusy means removal needs the caller's explicit cancellation choice.
var ErrRoleTextKept = errors.New("a local override kept its value")
ErrRoleTextKept reports a role-file import refused because a local override stands under the role's text: it keeps its value and the user resolves the two (§5.9: neither wins silently). The record returned beside it is the kept local one.
var ErrSlotHeld = errors.New("the foreman slot is held by an attached chat")
ErrSlotHeld marks a foreman turn the engine will not submit because an attached chat holds the slot (§4.2 item 1, §4.6): a headless turn only ever starts from a free slot.
var ErrTaskInstructions = errors.New("task instructions refused")
ErrTaskInstructions reports a task whose skills or prompt bounds the resolver refuses: an unknown or repeated skill, or instructions plus skills over a prompt's bound. The whole call that named the task is refused.
var JobStatuses = []string{
statusQueued, statusLeased, statusRunning,
statusDone, statusFailed, statusCancelled, statusStalled,
}
JobStatuses is the job statuses' fixed vocabulary, in the order the plain-text renderings print them: live first, then terminal. Status zero-fills its counts over it, and the renderers — the client's status command, the MCP bridge's status tool — share the order.
var RoleKeys = []string{RoleForeman, RolePlanReview, RoleWorker, RoleReviewer}
RoleKeys are the roles, in the order show prints them.
var SettingKeys = []string{SettingApprovalMaxTasks, SettingApprovalBudget, SettingApprovalClasses, SettingApprovalProtectedPaths, SettingGatePlan, SettingGateLand, SettingGatePush, SettingPlanReview, SettingPlanReviewTier, SettingPlanReviewEffort, SettingApprovalTiers, SettingApprovalRestarted, SettingLandTarget, SettingAfterLand, SettingLandReady, SettingDefaultCheck, SettingWorkerTools, SettingSetup, SettingSetupCacheKeyFiles, SettingSetupTimeout, SettingExtraRepos, SettingForemanRotateTokens, SettingForemanWakesPerDay, SettingPlanFiles, SettingInstructions}
SettingKeys are the editable keys, in the order show prints them.
Functions ¶
func Canonicalize ¶
Canonicalize turns a repo URL into the project's canonical identity (design §13.1 item 1): scp-like SSH, ssh:// and HTTPS forms of one host and path give host[:port]/path. Userinfo is dropped and never appears in the result or in an error; the host is lowercased and the path is not; one trailing .git and trailing slashes go; a port stays only when it is not the scheme's default. A file:// remote, or a plain absolute path, gives file:<absolute cleaned path>. What the string alone cannot settle (a leading-slash or ~ scp path, an SSH config alias, a '@' in a path that may be an unescaped password) is an error, never a guess.
func CheckBaseFetchable ¶
CheckBaseFetchable reports why a task's base ref cannot be fetched by the land job (§7.1): the job fetches each ref by branch name (+refs/heads/<ref>), so a bare commit SHA, which names no branch, is never fetchable however reachable the commit is. Task submit, the plan tools and the land job all call it, so they cannot disagree.
func ConfigHash ¶
ConfigHash is the hash of a project.yaml's bytes, as the store records it for an import and as a client compares it for status.
func ConfigHashWith ¶
ConfigHashWith is ConfigHash for a file with an instructions_file: the hash covers the yaml and the text read from that path, so a change to the text alone counts as a changed file. With nil text it is ConfigHash.
func DefaultRoleFile ¶
DefaultRoleFile is the path a role's file has when its key is written with no value: .brigada/instructions/<role>.md.
func ListSnapshots ¶
ListSnapshots returns the snapshot files in dir, newest first. A missing directory holds none. Files that do not carry the snapshot name — a partial .tmp, anything else — are not snapshots.
func LookupProjectTx ¶
LookupProjectTx returns the id of the project a repo URL names, in the caller's transaction. sql.ErrNoRows when there is none.
func ProjectConfigDefaults ¶
ProjectConfigDefaults proposes init's defaults. Existing settings, comments and spelling survive; the caller shows the resulting diff and asks before writing. Empty input creates the minimal schema-1 file, with gate_push auto spelled out; an empty check omits or removes default_check so the project setting is unset.
func ProjectConfigGuidance ¶
ProjectConfigGuidance adds what init's defaults need to a project.yaml (§5.9, §5.10): the instructions map naming the role files at their default paths, and skills_dir. A file with no instructions map at all is first generation: it gains the whole map — or, when it keeps the 1.0 instructions_file, the three other roles only, the worker staying on its alias, for the file refuses naming both keys. Once the map exists a missing key is the user's choice: nothing is added back, so dropping a key reaches not configured at the next import, and init says so with the way to restore it (ProjectConfigRoleKey, the engine half of `brigada init --take-default <role>`). A key the map has is left as it is, and so is skills_dir.
func ProjectConfigRoleKey ¶
ProjectConfigRoleKey adds one role's key to a project.yaml's instructions map at the role's default path: the restore path of a role deliberately left out (§5.9), which `brigada init --take-default <role>` takes and init's plan names. A key the map already has keeps its path, and the worker stays on the instructions_file alias when the file carries one — a map key beside it is refused, as the file itself refuses both.
func RedactRepoURL ¶
RedactRepoURL returns raw with anything that may be a credential removed, for errors and logs about input that failed to parse: the userinfo (everything up to the last '@', so a password holding '/' or ':' goes too) and any query or fragment.
func Score ¶
Score is the success rate the dispatcher ranks agents by (§8): a rate with a prior of one pseudo-trial at 50%, (successes + 0.5) / (attempts + 1). With no attempts it is the prior itself, 0.5; each attempt moves it toward the observed rate, so a lone success reads 0.75, not 1.
func SnapshotPath ¶
SnapshotPath is the file a snapshot taken at t is written to.
func TaskCapsLabel ¶
TaskCapsLabel renders a task's non-default caps compactly, as status shows them next to tier and effort: "fix 3, review 1, restarts 4". It is empty when the task overrides nothing.
func ValidReviewFamily ¶
ValidReviewFamily reports whether word is a review.family setting.
func ValidReviewTier ¶
ValidReviewTier reports whether word is a review.tier setting.
func ValidateInstructions ¶
ValidateInstructions checks an instruction text the way the engine does: valid UTF-8, no NUL, at most MaxInstructionsBytes. The refusal names the size and the limit.
func ValidateRoleText ¶
ValidateRoleText checks a role text the way a set or an import does: a known role, then validateRoleText's bounds. A client calls it to refuse before it sends; the engine checks again.
func ValidateSettingValue ¶
ValidateSettingValue reports whether the key and value would be accepted, so the CLI can refuse before it contacts the engine.
func ValidateSkills ¶
ValidateSkills checks a skill import whole, as ImportSkills will, so a caller can refuse before it writes anything else. The refusal names the skill.
Types ¶
type AgentAdvert ¶
type AgentAdvert struct {
Adapter string
Harness string
Model string
Family string
Account string
Classes []string
// Effort is the reasoning-effort level the agent runs at; empty when
// unset. It is not part of the agent's key: outcomes group by harness
// and model.
Effort string
// Tools is the claude worker scope the runner recorded (§5.2), the
// patterns joined by "+" or "none"; "" for a harness brigada does not
// scope.
Tools string
}
AgentAdvert is one advertised (adapter, harness, model) combination a runner offers (§5.1): what the dispatcher will choose between (§8). The account label names the quota pool — cooling covers the whole account, not one agent — and family is the model family a reviewer must differ from (§7).
type AmendPlanResult ¶
type AmendPlanResult struct {
PlanID int64
State string
Added []CreatedTask
Cancelled []int64
Retargeted []int64
Retiered []int64
Respecced []int64
Started []CreatedTask
Held []HeldTask
Reasons []string
}
AmendPlanResult is what AmendPlan did: the plan's state after it, the added tasks, the ids cancelled, re-pointed or retiered, and every task the amendment started (a work job queued), added or not. Reasons says why the plan waits for approval, when it does; Held names the added or respecified tasks a plan rule held. Respecced lists the tasks whose spec was edited.
type Answer ¶
type Answer string
Answer is how an engine call went. Errors are reserved for the store and for caller mistakes (a malformed complete); a runner's protocol outcome — lost, gone, fenced out — is an answer, so the caller can branch on it instead of parsing error text.
const ( AnsOK Answer = "ok" AnsAlready Answer = "already" // the idempotent repeat of a call that took effect AnsCreated Answer = "created" // submit: a queued job now exists AnsPending Answer = "pending" // submit: a live job holds the key; the follow-up flag is set AnsBusy Answer = "busy" // attach: the foreman is already spoken for AnsCapped Answer = "capped" // submit: the task's round cap is spent; nothing was created AnsCancelled Answer = "cancelled" // the job was cancelled AnsGone Answer = "gone" // no such job, or it ended without the caller's part in it AnsLost Answer = "lost" // the lease belongs to someone else now AnsStaleEpoch Answer = "stale_epoch" // fenced out by a recover (§6.4) AnsExpired Answer = "expired" // attach release: the hold lapsed first; the session id was still written )
type AnswerResult ¶
AnswerResult is what Answer did: the questions row it filled — the worker's question and the foreman's answer together (§11) — and the job it queued, the fix job that resumes the author's session.
type ArtifactInfo ¶
type ArtifactInfo struct {
ID int64 `json:"id"`
JobID int64 `json:"job_id"`
ProjectID int64 `json:"project_id"`
Kind string `json:"kind"`
Path string `json:"path"`
Size int64 `json:"size"`
SHA256 string `json:"sha256"`
CreatedAt string `json:"created_at"`
}
ArtifactInfo is one stored artifact as the API shows it (§9): the row behind a transcript or check log, with the blob's content address.
type AskResult ¶
type AskResult struct {
ProjectID int64 `json:"project_id"`
EventID int64 `json:"event_id"`
Outcome WakeOutcome `json:"outcome"`
// HoldID names the attach that holds the foreman when the message
// waits for it (§4.2 item 3); 0 otherwise.
HoldID int64 `json:"hold_id,omitempty"`
// LimitedUntil is the reset time (RFC3339) of a limited foreman
// (§4.4); empty when the outcome is limited and the time unknown, and
// for every other outcome.
LimitedUntil string `json:"limited_until,omitempty"`
}
AskResult is what a user message did (§4.2 item 3): the project it went to, the event that holds it, and the wake's outcome — woken, or why it waits.
type AttachResult ¶
type AttachResult struct {
Answer Answer
HoldID int64 // the created hold; a busy second attach names the live hold
JobID int64 // a busy answer names the live foreman turn, when that made it busy
// ForemanToken is the hold-scoped foreman token (§12.1): it works
// while the hold lives and dies with the hold. Empty on busy.
ForemanToken string
RepoURL string
DefaultBranch string
LandTarget string // the effective land target; the default branch when unset
Harness string
Model string
Effort string // the foreman's reasoning-effort level; ” = unset
SessionID string // the key's current harness session id; ” = none
Prompt string // the opening prompt
ExpiresAt time.Time
}
AttachResult is what Attach hands the caller (§4.2 item 1): the busy answer carries what made it busy — a live turn's id, or a live hold's — while a created hold carries everything the client needs to run its interactive session: the hold's id, the hold-scoped foreman token (the secret's one appearance, as in the lease grant), the foreman's harness and model, the key's current harness session id to resume — empty when the key never ran — and the opening prompt, the foreman instructions plus the wake template with the project's pending events, the same composition a headless turn gets.
type CanonReport ¶
type CanonReport struct {
// Rewritten counts the projects whose repo_url or transport changed.
Rewritten int
// Collisions are groups of project ids that canonicalize to the same
// repository, by canonical form. Their rows are left untouched.
Collisions map[string][]int64
// Failed maps a project id to why its repo_url did not canonicalize
// (the URL itself is never quoted). Those rows stay as they are.
Failed map[int64]string
}
CanonReport is what the startup identity rewrite did (design §13.1).
type CapacityAgent ¶
type CapacityAgent struct {
ID int64 `json:"id"`
RunnerID int64 `json:"runner_id"`
Harness string `json:"harness"`
Model string `json:"model"`
Tier string `json:"tier"`
// Effort is the agent's configured reasoning effort from its runner's
// --agent effort=, or "default" when none is set.
Effort string `json:"effort"`
Account string `json:"account"`
// Cap is the effective cap — the smallest of the runner, account
// and model caps on the agent's path — and CapLayer names the
// layer that sets it (the caps package's layer names, or "none").
// A nil Cap means no layer caps the agent; a Cap of 0 means capped
// at nothing, which is different from not capped (§8.1).
Cap *int `json:"cap"`
CapLayer string `json:"cap_layer"`
// Running counts the jobs on the agent — leased or running, the
// ones that recorded it. A queued job holds no agent and never
// counts (§12.2).
Running int `json:"running"`
// Quota is the agent's quota state (§8): ready, cooling — with
// CoolingUntil, the store's UTC text — or broken. Broken wins over
// cooling: a broken agent takes nothing whatever its cooling says.
Quota string `json:"quota"`
CoolingUntil string `json:"cooling_until,omitempty"`
// Cost is the agent's cost weight (§8.1): the configured weight of
// its (harness, model) pair, 1 when the config names none.
Cost int `json:"cost"`
// Tools is a claude worker agent's shell scope as its runner
// advertised it (§5.2): Claude Code patterns joined by "+", or
// "none". Empty for a harness brigada does not scope.
Tools string `json:"tools,omitempty"`
}
CapacityAgent is one advertised agent as capacity reports it (§12.2).
type CapacityHeadroom ¶
type CapacityHeadroom struct {
ProjectID int64 `json:"project_id"`
Account string `json:"account"`
Cap *int `json:"cap"`
}
CapacityHeadroom is one reserved account as capacity reports it (§8.2): the foreman's account of a project and the cap task workers see on it — one lower than the account's cap, never below 0. An uncapped account has no slot to reserve and is not listed.
type CapacityLayer ¶
type CapacityLayer struct {
Layer string `json:"layer"` // the caps package's layer names: runner, account, model, plan, global
// RunnerID names a runner layer's instance, Account an account's,
// Model a capped model's and PlanID an active plan's.
RunnerID int64 `json:"runner_id,omitempty"`
Account string `json:"account,omitempty"`
Model string `json:"model,omitempty"`
PlanID int64 `json:"plan_id,omitempty"`
// Cap is the instance's cap; nil means the layer is not capped. A
// runner entry carries the folded cap — the smaller of the
// config's and the one the runner advertised (§8.1).
Cap *int `json:"cap"`
// Use counts the live jobs through the instance; Queue counts the
// queued jobs the instance holds (§12.2).
Use int `json:"use"`
Queue int `json:"queue"`
}
CapacityLayer is one cap layer's row as capacity reports it (§12.2): exactly one of RunnerID, Account, Model and PlanID names the instance, and all stay zero — empty — for the global layer.
type CapacitySnapshot ¶
type CapacitySnapshot struct {
Agents []CapacityAgent `json:"agents"`
Layers []CapacityLayer `json:"layers"`
// Headroom lists the accounts reserved for a foreman (§8.2), one
// per project and account; empty when no project has one or the
// rule is off.
Headroom []CapacityHeadroom `json:"headroom,omitempty"`
}
CapacitySnapshot is the foreman's capacity view (§4.5, §12.2): a read-only copy of what the dispatcher would see right now. Agents carries one entry per advertised agent — its effective cap and the layer that sets it, the jobs running on it, its quota state and its cost weight and tier. Layers carries one entry per cap layer — every runner, every account, every capped model, every active plan of the project, and global — with the layer's cap, its use and its queue depth. The percent-of-window quota figures §12.2 names ride on CodexBar data and are not part of the snapshot yet.
type Completion ¶
type Completion struct {
Usage *adapter.Usage
WallSeconds *float64
Result string
Error string
CheckResult string
SessionID string
Outcome string
// ErrorKind sorts an Error — "limit" when the CLI hit its
// subscription limit (§4.4), "setup" or "infra" when the job's own
// environment or plumbing failed (§5.7, §5.1) — and LimitUntil is
// the reset time then, RFC3339, empty when unknown.
ErrorKind string
LimitUntil string
// StaleDropped names the target branch whose stale waiting ref a
// land job deleted (§7.1): the landings that waited on it land again.
StaleDropped string
}
Completion is a runner's answer for a job (§5.4, §5.5, §12.3): exactly one of Result or Error, the check result when the runner ran the task's check, and the harness session the run ended on — written back so the next session-mode job on the key resumes it (§4.2). Artifacts are not the completion's to name: the runner uploads them through the artifact endpoint (§9), which stores them itself, so a runner can never point the engine's readers at a path of its choosing. Outcome, when set, names a run the runner stopped itself (§5.6): stalled, or past its class time cap. It rides with a Result — the stopped run's outcome block — and ends the job as stalled rather than done.
type Config ¶
type Config struct {
// Now is the engine's clock; tests inject one so they never sleep.
Now func() time.Time
// LeaseTTL is how long a lease lasts without a heartbeat (§5.1).
LeaseTTL time.Duration
// ReleaseExclude is how long a releasing runner doesn't see the job
// it gave back: other runners go first, and a lone runner may retry
// once the exclusion lapses.
ReleaseExclude time.Duration
// MaxAttempts is how many leases a job may lose before it fails as
// "runner lost N times" instead of being offered again.
MaxAttempts int
// QueueTTL is how long a queued job waits for a lease before it
// fails, so it cannot wedge its key forever.
QueueTTL time.Duration
// HardCaps is the wall-time cap per job kind, measured from the
// job's current lease (§5.6). Entries here override the
// defaults for their kind only.
HardCaps map[Kind]time.Duration
// RoundCaps are the default per-task round caps; a task's caps JSON
// (fix_rounds, review_rounds, restart) overrides them per task.
RoundCaps RoundCaps
// ExactRoundCaps, when set, replaces the default round caps as given,
// zeros included (RoundCaps reads zero as "the default"). The config
// file's rounds.* keys arrive here.
ExactRoundCaps *RoundCaps
// Review is the engine-wide default tier, effort and family rule of a
// review (§7, §8, issue #126); a task's own review_tier and
// review_effort win over its tier and effort.
Review ReviewConfig
// TaskAutoRetry retries a transient work failure once on another ready
// agent (§3). Nil keeps the default, on; false parks immediately.
TaskAutoRetry *bool
// PlanReview is the engine-wide default of the plan review (§6.2):
// nil keeps the default, on. A project's plan_review setting
// overrides it per project.
PlanReview *bool
// ForemanWakeBudget is how many foreman turns a project may be woken
// into per UTC day (§4.3); zero — the default — keeps
// defaultForemanWakeBudget. The count is of the project's foreman
// jobs, every one of them: turns this engine wakes and turns a person
// submits by hand alike.
ForemanWakeBudget int
// Caps are the layered concurrency caps and cost weights (§8.1),
// as the config named them; the zero value caps nothing. A runner's
// own advertised cap folds into the runner layer.
Caps caps.Limits
// DataDir is where the engine keeps its artifact blobs (§9), laid
// out content-addressed under artifacts/<sha256[:2]>/<sha256>. Zero
// derives the directory of the store file; a store with no file of
// its own keeps zero, and artifact uploads answer that plainly.
DataDir string
// MaxArtifactBytes caps one artifact upload (§9); zero keeps the
// 64 MiB default.
MaxArtifactBytes int64
// Retention is how long messages and artifacts are kept before the
// sweep prunes them (§9); zero — the default — keeps everything.
// The event log is never pruned.
Retention time.Duration
// NewLeaseID mints lease ids: <owner>-<random hex>.
NewLeaseID func(owner int64) (string, error)
// NewLeaseToken mints the secret of a lease-scoped foreman token
// (§12.1): random hex, returned once in the grant and stored only
// as its sha256.
NewLeaseToken func() (string, error)
// Rand is the uniform source, a float in [0, 1), that the agent
// ranking samples from (§8); tests inject it to pin the pick.
Rand func() float64
// LimitCooling is how long a task job's limit with no known reset
// cools its account (§8 "Quota"); zero keeps 30 minutes.
LimitCooling time.Duration
// FailureStreak is how many consecutive failed completions by one
// agent mark it broken (§8 "Health"); zero keeps 2.
FailureStreak int
// RunnerStale is how long a runner may go unseen before the sweep
// marks its agents broken (§8 "Health"); zero keeps 15 minutes.
RunnerStale time.Duration
// ProbeDelay is how long an agent broken by repeated failures waits
// before its first probe (§8 "Health"); zero keeps 5 minutes.
ProbeDelay time.Duration
// ProbeMaxDelay caps the growing delay between probes after failed
// ones (§8 "Health"); zero keeps 1 hour.
ProbeMaxDelay time.Duration
// SweepInterval is how often the engine sweeps when the lifecycle's
// idle ticker drives it (§13): the tick interval the sweep's
// suspension check measures a gap against. Zero keeps the default,
// the lifecycle's idle tick.
SweepInterval time.Duration
// PlanFiles turns the plan-file mirror on (§6.4): after each plan
// change the engine pushes the plan's file to the project's remote.
// Off by default — the mirror runs git against the project's repo URL —
// and on in the running engine.
PlanFiles bool
// PlanFileRetry is how long a failed push holds the next attempt back;
// zero keeps 30 seconds.
PlanFileRetry time.Duration
// PlanFileTimeout bounds one plan's fetch, commit and push; zero keeps
// two minutes.
PlanFileTimeout time.Duration
// SnapshotDir is where the engine snapshots its store (§6.4): a
// directory outside the store's own. Empty — the default — takes no
// snapshots.
SnapshotDir string
// SnapshotEvery is the interval between snapshots; zero keeps 24
// hours. Tests move it; the shipped engine does not.
SnapshotEvery time.Duration
// SnapshotKeep is how many snapshots stay; zero keeps 7.
SnapshotKeep int
// DefaultReviewer is the harness a standalone review (§7, `brigada
// review --branch`) goes to when the author's family cannot be
// told; empty = none, and such a review is then refused.
DefaultReviewer string
// BranchTrailers reads the Co-Authored-By trailer values of the
// commits a branch holds beyond its base, one per line, from the
// repo at repoURL; a standalone review's author-family fallback.
// Nil keeps the git-backed default; tests inject it.
BranchTrailers func(ctx context.Context, repoURL, base, branch string) (string, error)
// IssueFetch reads one issue or pull request of the repository at
// repoURL (§12.2 issue_read): GET requests alone. Nil keeps the
// gh-backed default — the user's own gh login on the engine's host;
// tests inject it so no test reaches the network.
IssueFetch func(ctx context.Context, repoURL string, number int, comments bool) (*Issue, error)
// Warnf logs the warnings of work the engine does best-effort, the
// plan-file mirror's failed pushes among them; nil logs to the
// standard logger.
Warnf func(format string, args ...any)
}
Config tunes the engine. The zero value means "the default for this field": New fills those in, so Config{Now: clock} alone is a fully configured engine on an injected clock.
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns the shipped settings (design §5.6 and §6.1): a 90 s lease, a 120 s release exclusion, three lost attempts, a 10 m queue TTL, hard caps of 45 m for work and fix, 25 m for review and 30 m for a foreman turn, round caps of fix 2 and review 2 (§7's default), restart 3, and artifact uploads capped at 64 MiB each. Retention keeps everything (§9); DataDir follows the store file.
type ConfigChange ¶
type ConfigChange struct {
Key string `json:"key"`
Old string `json:"old"`
New string `json:"new"`
}
ConfigChange is one key an import touches: Old is the store's value, New the file's (or "default" where the import resets the key).
type CoolingAgent ¶
type CoolingAgent struct {
ID int64 `json:"id"`
Harness string `json:"harness"`
Model string `json:"model"`
Account string `json:"account"`
Until string `json:"until"`
}
CoolingAgent is one agent the dispatcher is holding back for quota (§8, §10): it shows in status next to the running jobs, but not in the needs-you queue — cooling waits for a clock, not a person.
type CreatePlanResult ¶
type CreatePlanResult struct {
Answer Answer
PlanID int64
TaskID int64
JobID int64
Run int
Tasks []CreatedTask
// State is the plan's: "active", or planAwaitingApproval when the
// approval limit holds it (§6.3), with Reasons — one phrase each —
// saying why. A held plan queued no job: TaskID, JobID and Run are
// zero.
State string
Reasons []string
}
CreatePlanResult is what CreatePlan did: the plan, its tasks, and the first ready task's work job's own submit answer — created or, should a live job already hold the key, pending. TaskID, JobID and Run are that first ready task's: for the one-task plan, the task.
type CreatedTask ¶
type CreatedTask struct {
TaskID int64
Name string
JobID int64
// contains filtered or unexported fields
}
CreatedTask is one task CreatePlan made: its id, its name, and the work job queued for it — zero for a task that waits on dependencies.
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine is the lease engine (design §12.3, §6.4): the rules of the job queue — one live job per key, leases with fencing, coalescing submits, and the caps that keep a lost runner from wedging a key. Every operation is one transaction on the store and begins with a sweep, so expired leases and overspent caps are dealt with before anything reads or writes a job.
func (*Engine) AcceptPlan ¶
func (e *Engine) AcceptPlan(ctx context.Context, project, plan int64, reason string) (PlanDecision, error)
AcceptPlan is the foreman's plan_accept (§6.2, §12.2): a plan its review parked at needs_foreman is accepted as reviewed, with the reason recorded — and the plan proceeds exactly as a spent round's verdict does (planReviewAppliedTx): active with its first wave queued, or awaiting the user's yes when the approval rules hold it, the foreman told which. The reason is required, bounded like other notes; a plan not parked is refused whole. project scopes the plan to one project; 0 names any.
func (*Engine) AmendPlan ¶
func (e *Engine) AmendPlan(ctx context.Context, project, plan int64, am PlanAmendment) (AmendPlanResult, error)
AmendPlan is the foreman's plan_amend (§12.2): add tasks, cancel tasks re-point dependencies, change waiting or parked tasks' tier or effort, and replace or append to a pending task's spec, validated whole — names unique, deps in the plan and live, no cycle — before anything is written. Cancelling a task stops its live jobs. In the same transaction the waiting state re-evaluates as creation does (a pending task whose dependencies are all done turns ready and queues its work job), the approval limit is evaluated again for the amended plan, and a plan the amendment closed is finished. One plan_amended event names the change. project scopes the plan to one project; 0 names any.
func (*Engine) Answer ¶
func (e *Engine) Answer(ctx context.Context, project, taskID int64, text string) (AnswerResult, error)
Answer is the foreman's answer (§3, §12.2) for a task parked at needs_foreman by its worker's question: the question and the answer are kept together in questions, and the job that carries the answer resumes the author's session on the author's agent — the placement a fix round pins (§3) — its prompt the worker template with the spec and the answer verbatim in its own delimiters, introduced as the foreman's answer to the question (§1.4, §5.3). The resume is no fix round: it spends none of the task's budgets, so even a task out of fix rounds can be answered. The task returns to ready for the job's lease to take, and the task_answered event names the questions row — the question's id and no prompt (§9). Question, row, job, event: one transaction, so a caller never sees an answer whose job failed to appear.
func (*Engine) ApprovePlan ¶
ApprovePlan is the user's yes (§6.3): the plan turns active and its first wave — the tasks with no dependencies — turns ready with its work jobs queued, exactly as an active plan's would have been, all in one transaction. project scopes the plan to one project; 0 names any. The foreman never calls it: confirmation is the user's.
func (*Engine) ApprovePlanVersion ¶
func (e *Engine) ApprovePlanVersion(ctx context.Context, project, plan, version int64, stopAsking bool) (PlanDecision, error)
ApprovePlanVersion is ApprovePlan for the plan version the user was shown (§4.6): a plan amended since is refused whole with ErrPlanVersionStale. Version 0 names no version and checks nothing. stopAsking also changes, through the `brigada project set` path, the one setting each hold reason maps to (§6.3), so the same question is not asked twice; without it the approval behaves as it always has.
func (*Engine) ArchiveProject ¶
ArchiveProject removes a repository from active service without deleting its history. With cancel, live jobs and unfinished plans end through the same cancellation paths used by job cancellation and plan rejection. Removal and cancellation are atomic, including ending a live attach hold.
func (*Engine) Artifact ¶
Artifact reads one artifact's row and opens its blob for streaming (§9). Where the blob lives is derived from the row's digest, never read from the row: the path a row carries is bookkeeping, not a capability, so nothing that ever touched the store can point the engine's readers at a file of its choosing. The caller closes the file. nil, nil, nil when there is no such artifact — its digest is not a content address, or its blob is gone (pruned, lost) — which reads as not-found from outside, not as the engine's failure.
func (*Engine) AskForeman ¶
AskForeman puts the user's message in the project's foreman inbox and evaluates a wake for it (§4.2 item 3), in one transaction: the event that holds the words, its inbox row, the history's message with author user (§9), and the wake. The project is the one whose repo URL is repoURL, or, with none named, the engine's only project; source is the label of the client token that sent it (§12.2), carried into the foreman's prompt. The message reaches a foreman session apart from the judgment events, as the user's request (§1.4); who may send one is the API's to enforce — client and admin tokens only (§12.1).
func (*Engine) AskUser ¶
AskUser records a foreman question separately from user-originated messages. The harness presents it in chat; answering never follows from tool output.
func (*Engine) Attach ¶
Attach takes the foreman's key for the user's interactive session with it (§4.2 item 1). A second live attach is refused busy, and a live foreman turn makes it busy too — unless force, which cancels the turn through the cancel path: its delivered events return to pending without counting an attempt, and the attach's opening prompt takes them, so cancelling a turn to attach never drops a task worker's question. Otherwise the engine creates a hold, mints its foreman token, and puts the project's pending events — delivered to the hold — into the opening prompt. While a hold lives, wakeForemanTx submits nothing: events wait for the attached session.
func (*Engine) AttachHeartbeat ¶
AttachHeartbeat extends a live hold (§4.2 item 1): the hold's expiry and its foreman token's move out together, as a lease's and its token's do. A hold that was released or expired answers gone.
func (*Engine) BumpFenceEpoch ¶
BumpFenceEpoch raises the store-wide fence epoch (§6.4), for recover: from here on, every lease, heartbeat or complete that references the pre-recovery epoch is refused, so a late runner cannot write into the rebuilt store.
func (*Engine) Cancel ¶
Cancel stops a live job (§12.1): it goes to cancelled whatever lease is running on it, and the key's follow-up flag clears. The runner learns on its next heartbeat or complete. Cancel is not fenced: it carries no lease, so a client may cancel through a recover (§6.4).
func (*Engine) CanonicalizeProjects ¶
func (e *Engine) CanonicalizeProjects(ctx context.Context) (CanonReport, error)
CanonicalizeProjects rewrites every projects.repo_url to its canonical form in one transaction, and records the URL it was given as the project's transport where that is a usable Git URL (credentials dropped, never stored). Ids, plans, tasks, sessions and outcomes are untouched. Rows that canonicalize to the same value are not merged: the whole group is left as it is before anything is rewritten, and status lists it as needing the user. A row that fails to canonicalize stays as it is. Running it again changes nothing.
func (*Engine) Capacity ¶
Capacity reads the capacity snapshot (§12.2) in one read-only transaction: a copy of what the dispatcher would see, never a state change. projectID limits the plan layer to that project's active plans — the foreman's own project is what its token scopes it to (§12.1) — while 0 lists every active plan, for a caller that is not one project's foreman.
func (*Engine) CapsUsage ¶
CapsUsage builds the caps model's usage view (§8.1) in one read-only transaction: the live jobs — leased or running, not queued, which hold no runner, no agent and no slot — counted per runner (the lease's holder), per account and per model (the agent the lease recorded), per plan (the task's) and globally. The lease gate reads the same view beside the configured limits; nothing here changes the store.
func (*Engine) Complete ¶
func (e *Engine) Complete(ctx context.Context, owner, jobID int64, leaseID string, c Completion) (Answer, error)
Complete finishes a job as done or failed — or, when the completion carries a stop outcome, as stalled (§5.6). It is accepted while the lease id matches and the caller is the lease's holder, even after the lease expired and the job went back to queued, as long as nobody re-leased it: a runner returning from an outage delivers its answer instead of forcing a re-run (§12.3). It is idempotent per lease id, answers gone when the job was finished by someone else, lost after a re-lease or from a runner that does not hold the lease, and cancelled when the job was cancelled.
func (*Engine) CreatePlan ¶
func (e *Engine) CreatePlan(ctx context.Context, project int64, title string, tasks []PlanTask) (CreatePlanResult, error)
CreatePlan is the foreman's plan_create (§6.2, §12.2): one plan holding its tasks (§6.1), active from the start unless the project's approval limit (§6.3) holds it for the user's yes — then no job is queued and the plan_awaiting_approval event says why. A task with no dependencies is ready and its first work job is queued on the task's key, its prompt composed here from the versioned template plus the spec verbatim (§1.4, §5.3); a task with dependencies waits, pending, until the last of them is done. The graph is validated whole before anything is written: a dependency naming no task of the plan, a self-dependency or a cycle refuses the plan. Plan, tasks, edges, jobs, the plan_created event and the task_spec messages land in one transaction, so a caller never sees a plan half made.
func (*Engine) DueProbes ¶
DueProbes lists the runner's agents broken by repeated failures whose next probe is due (§8 "Health"), for the runner to probe and report. An auth-broken agent — which waits for the user's reset — is never listed, and neither is a failure-broken one whose probe time has not arrived.
func (*Engine) ForemanEvents ¶
ForemanEvents reads the project's pending inbox (§4.2): the one check the `events` tool's long-poll re-asks — the judgment events render exactly as the wake prompt renders them, the user's messages (§4.2 item 3) apart — and reading them here is consuming them (§4.2: events are consumed when the attached foreman reads them through `events`). An empty inbox says "no new events"; the API's long-poll loops on it, so a headless turn's token may call it too.
func (*Engine) ForemanSlot ¶
ForemanSlot reads the project's slot: what `brigada status` shows.
func (*Engine) Heartbeat ¶
func (e *Engine) Heartbeat(ctx context.Context, owner, jobID int64, leaseID string, prog Progress) (Answer, time.Time, error)
Heartbeat reports the runner is still on the job and returns ok with a new expiry, or why not (§12.3): cancelled first, then gone for a job that ended (lost, not gone, when this very call's sweep just hard-capped the lease the caller holds), lost for a lease id that is no longer the job's or one the caller does not hold — the lease id is the capability, the owner the identity, and both must agree — stale_epoch once a recover fenced the caller out (§6.4), and lost past the hard cap. An expired lease nobody re-leased is revived: the job goes back to leased with the accrual clock restarted. Progress from a heartbeat that answers ok is recorded on the row; the others carry no hold, so they record nothing.
func (*Engine) History ¶
History reads one page of the timeline (§9): events and messages merged, oldest first, as one story. Like every engine call it sweeps first, so the log it reads is the rules' current word. The next cursor is empty when the timeline is exhausted. In Last mode the page is the newest Limit items, still oldest first within it, and the next cursor is always empty: there is no forward continuation from the newest page. Read further back by narrowing to a task or job, or by raising the limit. Last cannot continue from a cursor.
func (*Engine) HoldTokenScope ¶
func (e *Engine) HoldTokenScope(ctx context.Context, secret string) (project, hold int64, ok bool, err error)
HoldTokenScope resolves the secret of an attach hold's foreman token to the project and hold it was minted for: what the API's authentication asks about a bearer secret the token file does not know, beside the lease tokens. ok is false for a secret nobody minted or one past its expiry — the expiry is checked here, at authentication time, so a token dies with its hold even before the sweep has expired the row.
func (*Engine) ImportProject ¶
func (e *Engine) ImportProject(ctx context.Context, repoURL string, content []byte, instructions *string, roles map[string]string, skills *[]Skill, accept bool) (ImportResult, error)
ImportProject is one whole import (§13.1): the project.yaml's settings — skills_dir included — the worker file's text as the instructions setting, every other role file the nested instructions map names (a key the file dropped moves its role to not configured), and the skills under skills_dir, all in one store transaction. A refusal or an error at any step rolls the whole import back and every previous value stays in effect (§5.9: an import is one step, never a half-applied one); the API reports the error as it does a config refusal alone. A local override that keeps its text is not a refusal: the conflict is reported in the result and the rest of the import commits.
instructions is the worker file's text, read by the client from instructions_file or the nested map's worker entry — whichever the file names, and roles[worker] stands in when only the map names it. roles holds each other role's file text. skills, when not nil, replaces the project's set; an empty set drops them all.
func (*Engine) ImportProjectConfig ¶
func (e *Engine) ImportProjectConfig(ctx context.Context, repoURL string, content []byte, accept bool) (ImportResult, error)
ImportProjectConfig imports a project.yaml's content for the repo (§13.1). The content is validated whole first; the file's repo must be the canonical identity of repoURL, the origin the client read. A file change over a value that is still the previous import's applies; over a local override it does not, and the conflict is returned; a first import over hand-set values returns them as Diffs and writes nothing until it is repeated with accept. Validation, settings and provenance land in one transaction.
func (*Engine) ImportProjectConfigWith ¶
func (e *Engine) ImportProjectConfigWith(ctx context.Context, repoURL string, content []byte, instructions *string, accept bool) (ImportResult, error)
ImportProjectConfigWith is ImportProjectConfig for a file that names an instructions_file (§5.9): instructions is the text the client read from that path, never a path for the engine to read. A file that names one with no text sent is refused; an empty text is an unset value. The text is bounded like a `project set` of it, and a refusal leaves the previous value in effect.
func (*Engine) ImportRoleFile ¶
func (e *Engine) ImportRoleFile(ctx context.Context, repoURL, role, text string) (*RoleInstruction, error)
ImportRoleFile imports the text of a role file project.yaml's nested instructions map names (§5.9), read and sent by the client, never a path for the engine to read. It covers the foreman, plan_review and reviewer roles; the worker's file is the instructions setting and imports with project.yaml itself (ImportProjectConfigWith), so the setting and the worker row stay one text. The text is bounded like a set of it and never truncated. An applied import records the imported source and its hash as the settings version; an empty file is the empty-by-choice state, imported. A local override wins: the import is refused with ErrRoleTextKept and the kept local record returned.
func (*Engine) ImportSkills ¶
ImportSkills replaces the repo's project's skills with an import of its skills directory (§5.10): every SKILL.md as text, with its name, description, digest and tree digest, read and sent by the client. A skill absent from the import is gone after it. One refused skill — over the 4096 byte bound, not UTF-8, a NUL, a bad name, a name given twice — refuses the import whole, truncates nothing and leaves the previous skills in effect. A re-import changes future resolutions only, never a created task.
func (*Engine) Job ¶
Job reads one job by id (§12.1); nil when there is none. The status it answers is the rules' current word on the job, not a row as it happens to sit — but the word changes only when a time-based rule could have bitten since the last write, so the read first asks sweepDue whether one could have (a lease past its expiry, a live job past its kind's hard cap, a queued job past the queue TTL) and only then sweeps, in a write transaction, before reading again. A row none of them can touch — a job still inside its lease, or already terminal — needs no sweep at all, so the read stays on the deferred read-only path and never waits on the store's write lock: a status poll cannot be starved by a lease poll or an upload, and it adds no writer of its own to their queue. The sweep here is for this job's row alone; the store-wide pass (attach holds, stale runners) belongs to the calls that write and the idle engine's ticker.
func (*Engine) LandApprove ¶
func (e *Engine) LandApprove(ctx context.Context, project, taskID int64, stopAsking bool) (LandDecision, error)
LandApprove releases a task held at the land gate (§6.3): the hold's row is deleted, the event names the release, and the land job is queued directly — the user's yes overrides the gate, which would otherwise re-hold the task. stopAsking also changes, through the `brigada project set` path, the one setting each hold reason maps to (§6.3), so the same question is not asked twice. The foreman has no tool for it: the gate is the user's.
func (*Engine) LandReject ¶
LandReject cancels the landing of a task held at the land gate (§6.3): the hold's row is kept, marked rejected, and the event records the cancellation. The task stays done and its branch is left alone — nothing is squashed, nothing is pushed, and the landing is never recorded.
func (*Engine) LeaseTTL ¶
LeaseTTL is the configured lease lifetime (§5.1), which is also how long dispatch is held after a recover.
func (*Engine) LeaseTokenScope ¶
func (e *Engine) LeaseTokenScope(ctx context.Context, secret string) (project, job int64, ok bool, err error)
LeaseTokenScope resolves the secret of a lease-scoped foreman token to the project and job it was minted for: what the API's authentication asks about a bearer secret the token file does not know. ok is false for a secret nobody minted or one past its expiry — the expiry is checked here, at authentication time, so a token dies at its lease's expiry even before the sweep has collected the row.
func (*Engine) LiveAttaches ¶
LiveAttaches counts the live attach holds (§4.2 item 1), which is the §13 idle rule's "attached sessions": a heartbeated hold is a user's open chat with the foreman, and the engine that served the attach stays — the hold's token, its events tool and its wake rule all answer on this engine's socket. A hold past its expiry is the sweep's to settle, not a session still attached.
func (*Engine) LiveJobs ¶
LiveJobs counts the live jobs — queued, leased and running — which is the §13 idle rule's "no live jobs": a job waiting for a runner counts as work exactly like one a runner holds.
func (*Engine) MarkDraining ¶
MarkDraining records that the runner has a restart drain pending (§5.1): it takes no new lease and exits once its running jobs finish. The mark is the engine's memory of a runner's own report — not store state — because it can only be true while that engine lives: a restarted engine meets either the draining runner's next report or its replacement's register, and the drain itself needs no engine at all.
func (*Engine) MarkRecovered ¶
MarkRecovered is the last step of a recover (§6.4), in one transaction: it bumps the fence epoch, so a late runner's lease, heartbeat or complete from before the recovery is refused, and stamps the recovery time, from which the engine holds dispatch for one lease-expiry window (dispatchHeldTx) so the runners whose heartbeats now fail have killed their workers before anything is leased again.
func (*Engine) MaxArtifactBytes ¶
MaxArtifactBytes is the configured cap on one artifact upload.
func (*Engine) PlanWorkerSeconds ¶
PlanWorkerSeconds sums the leased wall time the plan's jobs have banked, the plan's worker time. Foreman turns belong to no task, so they are not worker time.
func (*Engine) ProjectForeman ¶
ProjectForeman reads the foreman's harness and model back (§4.1); nil when the repo has no project. Like every engine call it sweeps first, so the answer is the rules' current word, not a row as it happens to sit.
func (*Engine) ProjectSettings ¶
ProjectSettings reads the project's settings; nil when the repo has no project.
func (*Engine) Projects ¶
func (e *Engine) Projects(ctx context.Context) ([]ProjectInfo, error)
Projects lists active projects, oldest first, each with its foreman, its active plans and its needs-you count — the count the status shows for that project alone.
func (*Engine) Prune ¶
Prune applies the retention policy by hand (§9): messages and artifacts created before the cut go — the artifacts' blob files with them — and the event log keeps everything, append-only and forever the backbone. A blob another row still references stays: content addressing means one file can back many rows. The sweep also takes the upload temps crashed writers left behind, once older than any upload still in flight.
The counting and the file removal run inside the prune's own transaction, not after it: the store serialises writers, so an upload of the same content cannot commit a row between the count that spare the file and the remove that takes it. The trade is that a transaction which fails after removing files rolls the rows back while the blobs stay gone — read as not-found (Artifact), never as corruption. File removal itself is best-effort; the rows are gone whatever it answered.
The transaction runs under the bounded busy retry, on a context of its own within writeDeadline, as the sweep's maintenance transaction does — and apart from it, so a prune refused busy never re-runs a maintenance pass that already committed. Each attempt starts its report clean, so PruneReport.Files counts two things: the blob files the committed attempt's transaction removed, and the stale upload temps sweepStaleTemps takes after the commit. A blob an attempt removed before it rolled back is not counted: it stays gone while its rows survive that attempt — the trade above — and the retry deletes those rows again, its remove of the missing file a no-op.
func (*Engine) PushApprove ¶
PushApprove releases the push gate for the project's waiting landings (§6.3): each landing recorded as waiting gets a push job — the land job reused, its push phase — which a runner leases and completes by pushing the recorded commit, no new squash. The foreman has no tool for it: the gate is the user's. The waiting landings stack on the remote's waiting ref, each on the one before, so their pushes are fast-forwards in any order and on any runner: a push whose commit a later landing already carried is pushed already. repoURL names the project, as every client command does.
func (*Engine) ReadIssue ¶
func (e *Engine) ReadIssue(ctx context.Context, projectID int64, number int, comments bool) (Issue, error)
ReadIssue reads one issue or pull request of the project's own repository (§12.1 issues, §12.2 issue_read): number names it on the forge the project's repo URL names, comments asks for the comments too. The repository is never an argument — the project names it, and the caller names the project, so a foreman token's own project is the one repository it can read. Every reason the read fails comes back as an error the caller can report; nothing is searched as a fallback.
func (*Engine) RecordInstance ¶
RecordInstance notes which process is the engine and since when (§13): the lifecycle writes it once it holds the single-instance lock, and `brigada status` reads it through the API.
func (*Engine) RecordLanded ¶
func (e *Engine) RecordLanded(ctx context.Context, repoURL, branch string, landed []LandedCommit) error
RecordLanded records landings the runner detected from the landing branch's trailers — the land target when one is set, else the default branch (§7.1): a hand squash, or a brigada land in the user's clone, whose commits the land job never made. Each entry whose task belongs to the project repoURL names is written as a landing record — the engine's "landed" mark, carrying the commit — unless one already exists, so a task the land job landed is never recorded twice. A landed record from either source counts: the land rule, a dependent's wait and the status all read the one table.
func (*Engine) RecoverForemanWakes ¶
RecoverForemanWakes is the start-of-engine recovery (§13): after the sweep, every project with pending inbox rows and no live foreman turn gets its wake evaluated — an event that parked a task while no engine ran does not wait for someone to look at `brigada status`.
func (*Engine) RecoverPlans ¶
func (e *Engine) RecoverPlans(ctx context.Context, extraRepos []string) (results []PlanRecovery, warnings []string, err error)
RecoverPlans applies the plan files of every project in the store, and of each repo named in extraRepos (a repo with no project gets one, its foreman unchosen), reading each from the project's plans branch. Fetch failures and unreadable files come back as warnings: recovery rebuilds what it can.
func (*Engine) Register ¶
func (e *Engine) Register(ctx context.Context, name string, maxParallel int, agents []AgentAdvert) (int64, error)
Register upserts the runner's row and its advertisement (§5.1): registering again refreshes last_seen and keeps the id, and the agents the call carries replace the runner's set, keyed by runner, adapter and model (§11). Agents the new set no longer names are deleted; ones it keeps keep their rows, so the outcomes that reference them survive a restart. maxParallel is the runner's own cap on parallel jobs (§8.1): 0 means it advertised none. The row's id is what every lease the runner takes references, so a runner must register before it can lease.
func (*Engine) RegisterStarted ¶
func (e *Engine) RegisterStarted(ctx context.Context, name string, startedAt time.Time, maxParallel int, agents []AgentAdvert) (int64, error)
RegisterStarted is Register for a runner that reports when its process started. The engine keeps that time as the runner's started_at and moves it only when the reported time differs from the stored one: a re-poll of the same process leaves it, a restarted process sets it. A zero startedAt reports nothing and leaves the stored time alone.
func (*Engine) RejectPlan ¶
RejectPlan is the user's no (§6.3): the plan and its tasks are cancelled; nothing was queued, so no job needs stopping. The foreman hears of it through its inbox.
func (*Engine) RejectPlanVersion ¶
func (e *Engine) RejectPlanVersion(ctx context.Context, project, plan, version int64) (PlanDecision, error)
RejectPlanVersion is RejectPlan for the plan version the user was shown; see ApprovePlanVersion.
func (*Engine) Release ¶
func (e *Engine) Release(ctx context.Context, owner, jobID int64, leaseID, reason string) (Answer, error)
Release gives a job back (§12.3): queued again with the attempt refunded and the lease cleared — a late heartbeat or complete from the releaser is lost — while the releaser does not see the job again until the exclusion lapses. The caller must be the lease's owner: a release under anyone else's name is lost, so released_by and the exclusion window can never be pinned on a runner that does not hold the lease. The reason is kept, bounded to 500 bytes. Idempotent per lease id.
func (*Engine) ReleaseAttach ¶
ReleaseAttach ends an attached chat (§4.2 item 1). The session id the client ended on is written back to the key's sessions row — created if the key never had one — because a resume forks session ids, and the next wake must resume the id the chat actually reached. The rows delivered to the hold are consumed: the attached foreman read them, or the chat would not end on them. The hold's token is revoked, and the wake is re-evaluated, for the events that arrived while the hold lived and were never read. A release that arrives after the hold lapsed — the laptop slept through the expiry — still writes the session id (the chat forked it whatever the engine did about the hold) and answers expired; the expiry has already returned the rows, revoked the token and re-evaluated the wake, and no detach is logged for a hold that was not live to detach.
func (*Engine) ReopenStrandedPlans ¶
ReopenStrandedPlans is the start-of-engine pass (§13, issue #137): in one store transaction, after the sweep as every recovery call does, it finds every plan in state done whose nodes still hold live work by the plan-close rule's own test (planLiveWorkTx, #133) and returns each to active. Every done plan is read, not only the ones known when the pass was written. For each reopened plan it bumps updated_at — a plan write the plan-file mirror's trigger (§6.4) marks owed, so plan-file recovery cannot restore `done` — and appends one plan_reopened event naming the plan and the task ids that kept it open. It writes no inbox row and wakes no foreman: the foreman's tools, not the foreman, were what the closed plan refused. It leaves cancelled plans and every other state untouched, and it does not touch the outcome record: a reopened plan's re-close counts once more on the plan class (§8), which is measurement only. A second run changes nothing — a reopened plan is active, and no done plan that settles stays behind. It returns the reopened plan ids.
func (*Engine) ReportProbe ¶
ReportProbe delivers one probe's result (§8 "Health"). A passing probe clears the broken state with a recovered event; a failing one grows the delay to the next probe — doubled, capped at ProbeMaxDelay — and one plain event names the next attempt. A report for an agent that is not this runner's, or not broken by repeated failures (an auth-broken agent, or one already reset), changes nothing.
func (*Engine) ResetAgents ¶
ResetAgents clears broken and cooling (§8: "or the user resets it") for the agents named by harness and model. The user's handle is the pair `brigada status` and `brigada stats` print, not an id: a model name shared by several runners resets all of them, which is what a user fixing one login or waiting out one quota means. Cooling is cleared on the matching agents only — their account siblings keep theirs until they reset or lapse. One plain event; ErrNoAgent when nothing matches. It returns how many agents matched.
func (*Engine) RestartTask ¶
func (e *Engine) RestartTask(ctx context.Context, project, taskID int64, note string) (RestartResult, error)
RestartTask is the foreman's task_restart (§3, §5.6, §12.2) for a task parked at needs_foreman for any reason: a fresh work job — a new attempt, fresh mode, its worktree built anew from the task's base — flagged restart, spending one of the task's restart budget. The new attempt carries its rounds unspent: the fix-round count is scoped to the fix jobs after the task's latest restart (§6.1), so the restart resets it. The foreman's note rides the prompt verbatim as the worker's feedback when there is one (§1.4, §5.3) and nothing rides when there is not. With the restart budget spent the call refuses and the task stays parked. Otherwise the task returns to ready and the task_restarted event lands, in the one transaction that queued the job.
func (*Engine) Retire ¶
func (e *Engine) Retire(ctx context.Context, project int64, tasks []int64, reason, caller string) (RetireResult, error)
Retire retires each named task of the project that has not landed, whatever its state — done, needs_foreman, needs_user, failed, held at the land gate, pending, ready, running — with the reason recorded verbatim and caller naming who asked, the foreman or the user. A landed task is refused, and so is a cancelled or already retired one: those are settled their own way. A task built on a retired task — a dependent, by dependency or base — blocks the retirement unless it is retired in this same call or is already cancelled or retired; every other state blocks, a failed dependent included, for its branch or its approved result is built on the retired task's branch. The refusal names each blocking task with its state, and one refusal rejects the whole call: nothing is written. Each retirement stops the task's live jobs, drops its land hold and leaves its branch alone; the plan then closes by the usual rule (§6.2), the done report naming the retired tasks with their reasons. project scopes the call to one project; 0 names any, and a task of another project is refused.
func (*Engine) RetryLanding ¶
RetryLanding queues again the landing of a task parked by a landing failure that is not about its content (#133): a task of project, in a plan not finished, parked at needs_foreman by a rejected push or by an environment failure, with no landing record. The task returns to done — off the needs-you queue, and off its environment cause the way a restart takes it off (the cause's episode ends once no task it parked is still parked) — releasing, as any finish does, the dependents that waited on it (§6.1): a pending one's work job is queued here. The land rule then runs as for any done task: the land gate and its approval rules, land_ready (the grant never skips it) and the push gate all apply. No gate is passed and no restart is spent.
The outcomes are four. queued: the land job is submitted, and the land_retried event names it. held: a land-gate rule held the task, the hold waiting for the user's release as any hold does, and land_retried carries the rule with no job. stale_base: the task's base is a superseded round of its dependency (issue #124), so the land rule refused the landing and parked the task again with its task_base_stale event — a recorded outcome, not a refusal, for the task did change state and land_retried records it; the way forward is task_restart. refused: nothing changed and nothing is recorded — a task of another project or a finished plan, one with a landing record (pushed or waiting at the push gate: task_restart makes a fresh attempt), one not parked at needs_foreman, held at the land gate (the user's), one with a live job, or one parked by anything but a rejected push or an environment failure — a conflict, a failed land check, a moved approved head or a stale base want task_restart.
func (*Engine) ReviewBranch ¶
func (e *Engine) ReviewBranch(ctx context.Context, repoURL, branch string) (ReviewBranchResult, error)
ReviewBranch queues a review of a branch with no plan, no task and no foreman behind it (§7, `brigada review --branch`). The author's family is the family of the agent of the branch's last work job; a branch with no such job, or one whose agent is gone or has no family, falls back to the Co-Authored-By trailers of its commits, and then to the config's default reviewer, which takes the review with no family rule. The review is placed by the review family policy (issue #126): under require an agent of another family alone qualifies; under prefer the family steps fall back to the author's own family and then the author's own agent, and a branch whose author's family cannot be told at all is placed on any ready agent offering the review class — the default reviewer's harness first when one is configured, as today. Among the agents that qualify — and, as for any review, the lease picks among them by the dispatcher's ranking — no such agent is ErrNoReviewer. The project is the one with repoURL, or the engine's only one.
func (*Engine) RoleInstruction ¶
func (e *Engine) RoleInstruction(ctx context.Context, repoURL, role string) (*RoleInstruction, error)
RoleInstruction reads the repo's effective text for one role, with its state, source, settings version, size and digest; nil when the repo has no project.
func (*Engine) RunnerID ¶
RunnerID reads the id of the runner named name; 0 when no such runner has registered.
func (*Engine) SetProjectForeman ¶
func (e *Engine) SetProjectForeman(ctx context.Context, repoURL, harness, model string) (ForemanSetting, error)
SetProjectForeman records the user's choice of the foreman's harness and model for the repo (§4.1). The harness must come from the resume-capable set — a foreman turn wakes headless — and neither the harness nor the model may be empty. The project is found by URL or created, born by setup with setupDefaultBranch. The setting lands with its event in one transaction, so the log never names a choice the row does not hold.
func (*Engine) SetProjectForemanEffort ¶
func (e *Engine) SetProjectForemanEffort(ctx context.Context, repoURL, harness, model, effort string) (ForemanSetting, error)
SetProjectForemanEffort is SetProjectForeman with the foreman's reasoning-effort level, validated against the harness's accepted set; empty leaves the CLI's own default. An effort change on the same harness keeps the session (only a harness change drops it).
func (*Engine) SetProjectSettings ¶
func (e *Engine) SetProjectSettings(ctx context.Context, repoURL string, values map[string]string) (ProjectSettings, error)
SetProjectSettings applies key=value pairs to the repo's project, all or nothing: every pair is validated first. An empty value resets the key to its default. Keys the settings row holds that this call does not name are kept. A repo with no project gets one, born by setup like the foreman choice's. Returns the settings as they now stand.
func (*Engine) SetRoleInstruction ¶
func (e *Engine) SetRoleInstruction(ctx context.Context, repoURL, role, text string) (*RoleInstruction, error)
SetRoleInstruction sets the repo's text for one role, all or nothing. The text is bounded at MaxRoleInstructionBytes, must be valid UTF-8 with no NUL, and is never truncated: a refusal names the role, the size and the limit, and the previous value stays. An empty text is an empty-by-choice mark, not a reset: the row stays and suppresses the role's default. The worker role mirrors the instructions setting, so every reader of the setting sees the same text. A repo with no project gets one, born by setup like the foreman choice's.
func (*Engine) SetWakeBudget ¶
func (e *Engine) SetWakeBudget(ctx context.Context, repoURL string, wakes int) (WakeBudgetInfo, error)
SetWakeBudget sets the project's daily foreman wake budget (§4.3) through the settings path, then re-evaluates the waiting wake at once, in the raise's own transaction: events parked by the spent budget reach a turn now, not at the next judgment event. A budget lower than today's use parks the next wake instead. The project is found by URL — empty names the engine's only project — and born by the set when the repo has none.
func (*Engine) Skills ¶
Skills lists the repo's project's imported skills by name. A repo with no project has none.
func (*Engine) SnapshotIfDue ¶
SnapshotIfDue takes a snapshot when one is due and returns its path, or "" when none was due or snapshots are off. After a success the snapshots beyond the keep count — the oldest — are removed; a failure holds the next attempt back for an hour and is returned.
func (*Engine) Stats ¶
Stats reads what the outcome record has learned (§8): per project, every outcome row joined with its agent and the agent's runner, and every registered agent's offered class with no outcome row listed at attempts 0. project scopes the answer to one project; 0 names every project. A read only: it changes nothing and does not sweep.
func (*Engine) Status ¶
Status reads the engine's own state (§13, §10). Like every engine call it sweeps first, so the counts are the rules' current word: a lease a dead runner left is expired here, not counted as work. project scopes the plans and the needs-you queue to one project; 0 names every project — the API passes a foreman caller's own project, and client or admin callers read all of them. The plans list carries every live plan and only the newest statusFinishedKeep finished ones; StatusAll lists every finished one.
func (*Engine) StatusAll ¶
StatusAll is Status with every finished plan listed, not only the newest statusFinishedKeep (§10): the status tools' all view — the MCP status tool's all parameter, `brigada status --all`. withText is StatusWithInstructions's flag.
func (*Engine) StatusWithInstructions ¶
StatusWithInstructions is Status with the full project instruction text in StatusInfo.Instructions (§5.9): the foreman reads it as data before it plans. Plain Status names source, version, size and digest only.
func (*Engine) Submit ¶
func (e *Engine) Submit(ctx context.Context, p SubmitParams) (SubmitResult, error)
Submit creates a queued job for the key, or coalesces behind the live one (§12.1): while a live job holds the key, nothing is created, the live job's follow-up flag is set and the caller learns which job to wait for. A submit past the task's round cap creates nothing and answers capped. Losing the one-live-job-per-key race to a concurrent submit returns the same pending answer, never an error.
func (*Engine) SubmitForemanTurn ¶
func (e *Engine) SubmitForemanTurn(ctx context.Context, project int64, events []string) (SubmitResult, error)
SubmitForemanTurn queues one headless foreman turn for the project (§4.2 item 2): a job of kind foreman on the key foreman:<project>, in session mode — the key's long-lived session (§4.1) — under the harness the user chose for the project, with a prompt composed from the versioned foreman instructions and wake template plus the events verbatim (§1.4). The one-live-job-per-key rule does the coalescing (§12.1): a wake while a turn runs sets the live job's follow-up flag and answers pending.
func (*Engine) SubmitTask ¶
func (e *Engine) SubmitTask(ctx context.Context, p TaskParams) (TaskResult, error)
SubmitTask creates one task and queues its first work job (§12.1, the debug path until the foreman exists): the project is found by repo URL or created, the task lands ready on a plan of its own, and the job — its prompt composed by the engine from the versioned template plus the spec verbatim (§1.4, §5.3) — is submitted on the task's key in the same transaction, so a caller never sees a task whose job failed to appear.
func (*Engine) Sweep ¶
func (e *Engine) Sweep(ctx context.Context) (SweepReport, error)
Sweep runs the engine's maintenance pass by hand. Every engine call runs it first; the exported form is for tests and for an idle engine keeping the store honest without leasing anything (§13). A configured retention (§9) rides along: the sweep prunes what aged out of its window, messages and artifacts, leaving the event log whole.
func (*Engine) SyncPlanFiles ¶
func (e *Engine) SyncPlanFiles()
SyncPlanFiles starts a background push of every plan whose file is behind the store; a no-op when the mirror is off. The engine does it after each call that changed a plan, and a starting engine once, to catch up on what an earlier run left behind.
func (*Engine) UnimportRoleFiles ¶
func (e *Engine) UnimportRoleFiles(ctx context.Context, repoURL string, configured map[string]bool) ([]RoleFileRemoval, error)
UnimportRoleFiles moves to not configured (§5.9 'Three states, no hidden fallback') every non-worker role whose key the request no longer configures: configured holds the roles the request configures — the keys project.yaml's instructions map still names — and any other role's imported row is removed, so dropping a key reaches not configured at the next import instead of pinning the last import's text forever. The worker is not this function's: its text is the instructions setting and imports with project.yaml itself (ImportProjectConfigWith), which removes its imported row the same way. A local override wins (§13.1): the row keeps its text and is returned as Kept, the caller's conflict, as ErrRoleTextKept is for a text the file still carries. The upgrade's seed of a default (source generated) is no import: it stands until an import owns the role, so a project that upgraded without rerunning init keeps its guidance.
func (*Engine) UploadArtifact ¶
func (e *Engine) UploadArtifact(ctx context.Context, owner, jobID int64, leaseID, kind string, data []byte) (Answer, *ArtifactInfo, error)
UploadArtifact stores one artifact blob of a run the caller holds (§9, §12.3): the session's transcript or the check's log. It is fenced exactly like a complete — the lease id must be the job's, the caller its holder, the fence epoch current — and accepted, like a complete, while a lapsed lease nobody re-leased still carries the id. The blob lands in a content-addressed file under the engine's data directory (artifacts/<sha256[:2]>/<sha256>), with the store row naming job, kind, path, size and digest; the same content for the same job and kind is idempotent — already, the first row back — and a transcript upload points the job's transcript ref at the row.
func (*Engine) WaitPlanFiles ¶
func (e *Engine) WaitPlanFiles()
WaitPlanFiles blocks until the running push pass, if any, has ended: a stopping engine calls it before closing the store, and a test before it reads the remote.
func (*Engine) WakeBudget ¶
WakeBudget reads one project's foreman wake budget (§4.3): the effective budget, today's use, the reset time and the waiting events. repoURL names the project; empty names the engine's only one, the way `brigada ask` resolves a project. ErrNoProject when none matches.
type ExtraRepo ¶
type ExtraRepo struct {
// Name is [a-z][a-z0-9_-]*; it names the checkout directory and the
// variable BRIGADA_REPO_<NAME>.
Name string `json:"name"`
Repo string `json:"repo"`
URL string `json:"url,omitempty"`
// Ref is a branch, tag or commit; empty = the repository's default
// branch.
Ref string `json:"ref,omitempty"`
}
ExtraRepo is one extra repository. A caller names Name, Repo and Ref; resolution fills Repo with the canonical identity (§13.1 item 1) and URL with the credential-free URL the runner fetches from, since the identity alone (host/path) is not one.
type ExtraReposSpec ¶
type ExtraReposSpec struct {
Repos []ExtraRepo `json:"repos"`
Source string `json:"source"`
Hash string `json:"hash,omitempty"`
}
ExtraReposSpec is a task's effective list as the task records it: the entries, where the list came from and, for the project default, the hash of the imported file it came from, so a later change to the setting leaves a recorded task alone.
type ForemanInbox ¶
type ForemanInbox struct {
Events []string `json:"events"`
UserMessages []foreman.UserMessage `json:"user_messages"`
}
ForemanInbox is the foreman's pending inbox as it reaches a session (§4.2): the judgment events, rendered as event lines — data — and the user's messages (§4.2 item 3), kept apart because they are the one input that is a request rather than data (§1.4).
func (ForemanInbox) Empty ¶
func (in ForemanInbox) Empty() bool
Empty reports whether the inbox holds nothing at all.
type ForemanSetting ¶
type ForemanSetting struct {
ProjectID int64 `json:"project_id"`
RepoURL string `json:"repo_url"`
Harness string `json:"harness"`
Model string `json:"model"`
// Effort is the foreman's reasoning-effort level; empty when unset.
Effort string `json:"effort,omitempty"`
// Unserved is set on the answer to a choice when no registered runner
// agent offers the foreman class for the chosen harness/model pair: the
// foreman's turns will wait until one does. It says nothing of a
// registered agent that is only busy, cooling or broken.
Unserved bool `json:"unserved,omitempty"`
}
ForemanSetting is a project's foreman harness and model (§4.1, §11): the user's choice of which CLI runs the foreman and which model it runs on. The harness must resume headless — a foreman turn wakes and continues the project's session without a terminal (§4.2).
type HeldJob ¶
type HeldJob struct {
ID int64 `json:"id"`
TaskID int64 `json:"task_id,omitempty"`
Kind string `json:"kind"`
Layer string `json:"layer"`
Since string `json:"since,omitempty"`
Agent string `json:"agent,omitempty"`
Until string `json:"until,omitempty"`
}
HeldJob is a queued job a full cap layer holds, as the status shows it (§10): the job, its task and kind, and the layer that has no room — "runner" is a job waiting for a runner slot, "cooling" one waiting for its agent's quota to reset: Agent ("harness/model") is the one whose cooling ends first and Until when; "no_pair" is a foreman turn waiting for a runner agent that offers its harness/model, named by Agent.
type HeldTask ¶
HeldTask is one task the amendment added or respecified that a plan rule held (§6.3): it parks at needs_foreman with Reason — the rule's name — while the plan stays active and the rest runs on.
type HistoryQuery ¶
type HistoryQuery struct {
ProjectID int64
TaskID int64
JobID int64
Since string
Limit int
Last bool
}
HistoryQuery is one read of the timeline (§9): the filters and the page. Every filter is optional; ProjectID and TaskID narrow to one project or task, JobID to one job's whole story, Since is the cursor the previous page ended on, and Limit bounds the page (kept between 1 and 500, default 100). Last asks for the newest page instead of the next one: the filters and Limit still apply, Since must be empty, and the page has no next cursor — to read further back, narrow with the task or job filter, or raise the limit.
type ImportResult ¶
type ImportResult struct {
// Hash is the hash of the content imported.
Hash string `json:"hash"`
// NeedsConfirm: a first import found hand-set values the file would
// replace (Diffs); nothing was written and the caller must repeat the
// import with Accept set.
NeedsConfirm bool `json:"needs_confirm,omitempty"`
// Applied lists the keys whose store value the import changed, hand-set
// replacements accepted on a first import included.
Applied []ConfigChange `json:"applied,omitempty"`
// Conflicts lists the local overrides that kept their value against a
// different one in the file: Old is the local value, New the file's.
Conflicts []ConfigChange `json:"conflicts,omitempty"`
// Diffs lists, on a first import, the hand-set values the file differs
// from: Old is the hand-set value, New the file's.
Diffs []ConfigChange `json:"diffs,omitempty"`
// Settings is the project's settings after the import; zero while
// NeedsConfirm.
Settings *ProjectSettings `json:"settings,omitempty"`
}
ImportResult is what an import did, or — with NeedsConfirm — would do.
type InfraRetry ¶
type InfraRetry struct {
ID int64 `json:"id"`
TaskID int64 `json:"task_id,omitempty"`
Kind string `json:"kind"`
NotBefore string `json:"not_before"`
Reason string `json:"reason,omitempty"`
}
InfraRetry is one queued work or fix job held out of leasing by the infra backoff (§5.1): NotBefore is when it may be leased, Reason the infrastructure failure's words, flattened to one line.
type Issue ¶
type Issue struct {
Number int `json:"number"`
Title string `json:"title"`
Body string `json:"body"`
// Author is who opened the issue, the forge's login.
Author string `json:"author,omitempty"`
// URL is the issue's page on the forge, when it gave one.
URL string `json:"url,omitempty"`
// PullRequest says the number named a pull request, not an issue:
// the same numbers space on GitHub either way.
PullRequest bool `json:"pull_request,omitempty"`
// Comments are the asked-for comments, oldest first, as the forge
// lists them.
Comments []IssueComment `json:"comments,omitempty"`
}
Issue is one issue or pull request of the project's own repository, as issue_read answers it (§12.2): the number, title and body, and the comments when they were asked for. All of it is text from the forge — external, untrusted — which the tool renders inside <issue> data delimiters, never as instructions.
type IssueComment ¶
type IssueComment struct {
Author string `json:"author,omitempty"`
Body string `json:"body"`
// CreatedAt is when the forge says the comment landed, RFC3339.
CreatedAt string `json:"created_at,omitempty"`
}
IssueComment is one comment on an issue or pull request (§12.2).
type JobInfo ¶
type JobInfo struct {
ID int64 `json:"id"`
ProjectID int64 `json:"project_id"`
TaskID int64 `json:"task_id"` // 0 for a foreman turn
Kind Kind `json:"kind"`
Mode Mode `json:"mode"`
Key string `json:"key"`
Status string `json:"status"`
Prompt string `json:"prompt"`
Harness string `json:"harness"`
Labels []string `json:"labels"`
Restart bool `json:"restart"`
Followup bool `json:"followup"`
Attempts int64 `json:"attempts"`
LeasedSeconds int64 `json:"leased_seconds"`
Result json.RawMessage `json:"result,omitempty"`
CheckResult json.RawMessage `json:"check_result,omitempty"`
LeaseExpiresAt string `json:"lease_expires_at,omitempty"`
// TranscriptID is the job's transcript artifact (§9), the run's own
// record of itself; 0 until the runner uploaded one.
TranscriptID int64 `json:"transcript_id,omitempty"`
// CheckLogID is the job's newest check log artifact (§9); 0 when the
// job ran no check or left no log.
CheckLogID int64 `json:"check_log_id,omitempty"`
// SetupLogID is the job's newest setup log artifact (§5.7); 0 when the
// job ran no setup.
SetupLogID int64 `json:"setup_log_id,omitempty"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
JobInfo is a job as the API shows it (§12.1): the row's public state. The lease's id and owner stay between the engine and the holder; the expiry is shown because a waiting client waits on it. Timestamps are the store's UTC text as stored.
type Kind ¶
type Kind string
Kind is the dispatch class of a job (design §11): the three worker kinds, which belong to a task, and the foreman turn, which belongs to a project alone.
type LandDecision ¶
type LandDecision struct {
TaskID int64 `json:"task_id"`
Released bool `json:"released"` // true for an approval, false for a rejection
Queued bool `json:"queued,omitempty"`
JobID int64 `json:"job_id,omitempty"`
Settings []string `json:"settings_changed,omitempty"`
}
LandDecision is what releasing or cancelling a land-gate hold did (§6.3): the task, whether the landing now runs (Released) or is cancelled — the branch left alone, the hold row kept as rejected so the rule never re-holds a landing the user refused — and the key=value pairs an approval that stops asking changed.
For an approval, Queued says whether the land job was queued, with its id in JobID; a live job already on the task's key queues nothing, and JobID then names that job.
type LandMessage ¶
type LandMessage struct {
Title string `json:"title"`
Spec string `json:"spec"`
Summary string `json:"summary"`
CommitMessage string `json:"commit_message,omitempty"`
Family string `json:"family"`
Model string `json:"model"`
}
LandMessage is the task data a land job's squash commit message is built from (§7.1): the fields of internal/landing's Task the engine knows without running git — what the hand-driven land reads through the API, read here from the task, its last author job and that job's agent. The runner adds the branch and its fetched head. CommitMessage carries the worker's own commit message (§5.4) when it wrote one; empty means the runner builds today's format.
type LandRetryResult ¶
type LandRetryResult struct {
TaskID int64 `json:"task_id"`
Outcome string `json:"outcome"`
JobID int64 `json:"job_id,omitempty"`
Reason string `json:"reason,omitempty"`
}
LandRetryResult is what RetryLanding did: one of the four outcomes, the land job a queued retry submitted, and the reason a held, stale-base or refused retry carries.
type LandedCommit ¶
LandedCommit is one landing the runner detected (§7.1): a commit on the project's landing branch — the land target when one is set, else the default branch — that carries the task's Brigada-Task trailer, whatever made the commit.
type LeaseGrant ¶
type LeaseGrant struct {
JobID int64
LeaseOwner int64 // the runner the lease belongs to
ProjectID int64
TaskID int64 // 0 for a foreman turn
Kind Kind
Mode Mode
Key string
Prompt string
LeaseID string
// LeaseExpiresAt is when the lease lapses without a heartbeat.
LeaseExpiresAt time.Time
// ForemanToken is the lease-scoped foreman token (§12.1, §12.2),
// minted with the lease of a foreman turn and empty for every task
// job. It is the secret's only appearance: it reaches the runner in
// the grant and lives in the runner's memory alone — never a log,
// an event or an error message, and the store keeps only its hash.
ForemanToken string
// Effort is the task job's mapped effort, with the agent default applied.
Effort string
// ForemanEffort is the project's chosen foreman effort level, set on a
// foreman turn's grant only; ” = none chosen, the runner agent's own
// effort stands.
ForemanEffort string
Harness string // the job's harness constraint; ” = any (§8)
Agent string // the harness of the agent the lease picked (§8); ” = none
// AgentAdapter and AgentModel name the picked registration exactly for
// every job kind; a harness alone can name several agents on a runner.
AgentAdapter string
AgentModel string
RepoURL string // the task's own repo, else the project's (§6.1)
BaseRef string // the ref the worktree starts from; for a review, the diff's base end (§7)
CheckCmd string // the check the runner runs after a done (§5.5); ” = none — always for a review
// WorkerTools is the project's worker_tools in its stored text, for a
// work or fix job; ” = unset, the harness's default scope.
WorkerTools string
// Setup is the environment step the runner runs before the worker and
// again before the check (§5.7); nil = the project had none.
Setup *SetupSpec
// ExtraRepos are the read-only repositories the runner checks out
// beside the worktree (§5.8): the task's recorded list, the project's
// default for a task-less review.
ExtraRepos []ExtraRepo
// ReviewBranch is the task's result branch a review job reviews
// (§7): the runner's worktree sits at it, fetched from the remote,
// and it is all the review reads — never pushed, never re-made.
// Empty for every kind but review: a work job's worktree makes its
// own branch. BaseRef and ReviewBranch together name the diff the
// review prompt tells the reviewer to run.
ReviewBranch string
// ReviewSince is, for a re-review (§7), the head the review it
// resumes judged: the runner's diff file holds the commits since it.
// Empty for every other job.
ReviewSince string
// LandTarget is, for a land job (§7.1), the branch the task's diff is
// squashed onto: land_target, or the project's default branch. Empty
// for every other kind.
LandTarget string
// DefaultBranch is, for a land job (§7.1), the project's default
// branch: the runner creates a land_target the remote does not have
// yet from it. A foreman turn and a plan review carry it too: their
// BaseRef is the land target, and the default branch is the fallback
// when the remote lacks it (issue #122). Empty for every other kind.
DefaultBranch string
// LandMessage is, for a land job's squash phase (§7.1), the task
// data the squash commit message is built from, as the hand-driven
// land builds it from the API. nil for the push phase and every
// other kind.
LandMessage *LandMessage
// LandCommit is, for a land job's push phase (§7.1), the commit the
// landing already recorded: the runner pushes exactly it, no new
// squash. Empty for the squash phase and every other kind.
LandCommit string
// Push is true for a land job's push phase (§7.1): the squash and
// check already happened, and the runner pushes the recorded commit,
// then runs after_land. False for the squash phase and every other
// kind.
Push bool
// GatePush is the project's push gate a land job reads (§6.3): auto
// pushes, ask records waiting for the user, never records the exact
// push command without pushing. Empty for every other kind.
GatePush string
// AfterLand is the repo-relative command the runner runs after the
// push, or after the landing when gate_push is never (§6.3). Empty =
// none.
AfterLand string
// LandReady is the repo-relative command the runner runs before
// landing (issue #87). Empty = none.
LandReady string
// SkipLandReady is the engine's mark that the land_ready gate's hold
// was already released by an approval (issue #87): the runner runs
// no command and lands. False on every grant but a released hold's.
SkipLandReady bool
// Branch is the branch the engine names a work or fix job to work
// on (§5.1): a work job makes it from the base ref, a fix round
// continues the task's current branch, and the runner builds and
// pushes exactly it — never a name of its own choosing. Named once,
// at the job's first lease, and kept by every later lease of the
// same job. Empty for a review, which reads ReviewBranch and moves
// nothing, and for a foreman turn, which belongs to no task.
Branch string
// ApprovedHead is, for a land job's squash phase (§7.1), the head of
// Branch the approving review judged, as the author job's result
// recorded it: the runner refuses to land any other. Empty when the
// result recorded none.
ApprovedHead string
Attempt int64 // leases taken on the job, this one included
// ResumeSessionID is the harness session the key's last run ended
// on (§4.2), for a session-mode job to resume; empty when the key
// never ran or the job is fresh.
ResumeSessionID string
// SessionGen is the task's session generation (§5.1): how many
// restarts the task has had at this lease, which names the attempt
// the job belongs to. Every job of one attempt — its work job, the
// fix rounds that resume the author's session, its reviews — leases
// under one generation, and a restart moves it, so the runner can
// name a worktree path the session's whole round shares: a harness
// session belongs to the directory it started in. 0 for a foreman
// turn and a task-less review, which no resume ties to a task.
SessionGen int64
}
LeaseGrant is a job plus the lease that took it: what a runner needs to run the job and to speak about it afterwards (§12.3). The task fields — RepoURL, BaseRef, CheckCmd — are what the runner's workspace and check are made of (§5.1, §5.5); Attempt is the lease count after this one, so a re-dispatched attempt can take a fresh branch name (§6.4).
type Mode ¶
type Mode string
Mode is how the runner starts the job (§5.6): a fresh session, or a resume of the key's session with its context kept.
type NeedsYouItem ¶
type NeedsYouItem struct {
Kind string `json:"kind"` // "task", "plan", "foreman" (a limited foreman, §4.4; ID is the project's, Title its repo URL) or "agent" (a broken one, §8; ID is the agent's, Title harness/model)
ID int64 `json:"id"`
State string `json:"state"`
Title string `json:"title"`
Reason string `json:"reason,omitempty"`
PlanID int64 `json:"plan_id,omitempty"` // the task's plan, when the entry is a task
// ForemanHolds is true for a task parked at needs_foreman that the
// foreman will handle (§10, foremanHolds.task): a live turn carries
// its event, or an attached chat holds the project. The user's queue
// leaves such a task out; the foreman's list keeps it.
ForemanHolds bool `json:"foreman_holds,omitempty"`
// A plan awaiting approval also carries what a coordinator shows the
// user and quotes back (§4.6): the project's repo URL, the
// plan's version — an amendment bumps it — and its tasks.
RepoURL string `json:"repo_url,omitempty"`
Version int64 `json:"version,omitempty"`
Tasks []StatusTask `json:"tasks,omitempty"`
}
NeedsYouItem is one entry of the "needs you" queue (§10): something a person must look at — a task parked at needs_foreman or needs_user, a plan waiting for approval, or a plan its review parked for the foreman (§6.2). Kind is "task" or "plan"; Reason is what the parked task's latest event said, flattened to one short line — the worker's question for a needs_help, the review's findings for review changes, the check's tail or the failure's words for the rest.
type PlanAmendment ¶
type PlanAmendment struct {
Add []PlanTask
Cancel []string
Retarget []PlanRetarget
Retier []PlanRetier
Respec []PlanRespec
// SkipApproved says the user themself approved the cancellations of
// this amendment: only a user's token can say so, never the foreman's
// own. Without it a cancelled task is an abandoned one, and the
// planner's outcome counts it against the plan.
SkipApproved bool
}
PlanAmendment is what the foreman changes in a live plan (§12.2): tasks to add (their Deps name tasks of the plan, old or added), tasks to cancel, waiting tasks whose dependencies are re-pointed, waiting or parked tasks whose tier or effort is changed, and pending tasks whose spec is edited.
type PlanDecision ¶
type PlanDecision struct {
PlanID int64
State string
Tasks []CreatedTask
Settings []string
}
PlanDecision is what approving or rejecting a plan did: the plan's new state and, for an approval, the first wave's tasks with the work job queued for each. Settings holds the key=value pairs an approval that stops asking changed (§6.3).
type PlanRecovery ¶
PlanRecovery is one plan file's outcome.
type PlanRespec ¶
PlanRespec edits the spec of a pending task, one with no job yet: Mode replace (the default) swaps the whole spec for Spec, append adds Spec after the current one. Task accepts a plan task's name or decimal id. The edit re-resolves the task's role texts and skills (§5.9, §5.10): Skills, in order, replaces the recorded skills — nil keeps them — and the current texts are copied whatever Skills says.
type PlanRetarget ¶
type PlanRetarget struct {
Task string
Deps []string
// Base names the one of Deps the task stacks on, as PlanTask.Base
// does; ” = the default rule. The task's earlier base goes with
// its earlier dependencies.
Base string
// Continue names the one of Deps whose author's session the task
// continues, as PlanTask.Continue does; ” = the earlier continued
// dep goes with the earlier dependencies.
Continue string
}
PlanRetarget re-points one waiting task's dependencies (§6.1): Deps replaces the task's whole dependency set, an empty list drops them all. Task and Deps name tasks of the plan by name, or by id as a decimal.
type PlanRetier ¶
type PlanRetier struct {
Task string
Tier string
Effort string
ReviewTier string
ReviewEffort string
// Caps merges key by key into the task's own caps (fix_rounds,
// review_rounds, restart; 0 to 10); nil changes none.
Caps map[string]int
// Skills, in order, replaces the task's recorded skills (§5.10) when
// the task has not started; nil keeps them. The re-resolution copies
// the current texts.
Skills []string
}
PlanRetier changes a waiting or parked task's tier, effort, review tier, review effort, caps or — for a task that has not started — skills. Empty fields preserve their current values; Task accepts a plan task's name or decimal id.
type PlanTask ¶
type PlanTask struct {
Title string
Name string // ” = unnamed: nothing can depend on it
Spec string
Check string
Class string // ” = implement
Tier string // ” = standard
Effort string // ” = the agent's own level
// ReviewTier and ReviewEffort are the task's own tier and effort for
// its reviews (§7, §8); ” = the engine default (review.tier,
// review.effort).
ReviewTier string
ReviewEffort string
// Caps overrides the engine's round caps for this task, key by key
// (fix_rounds, review_rounds, restart; 0 to 10); nil keeps them all.
Caps map[string]int
Deps []string
// Base names the one of Deps whose result branch this task's work
// starts from; ” = the only dep when there is exactly one, the
// continued dep when Continue names one, else the project's default
// branch.
Base string
// Continue names the one of Deps whose author's session this task's
// first work job resumes (§6.1): the worker that wrote the dep's
// approved result starts the new task warm, in its own session.
// ” = start fresh like any other task.
Continue string
// ExtraRepos are the read-only repositories the task is given (§5.8).
// nil = inherit the project default; non-nil, even empty, replaces it.
ExtraRepos []ExtraRepo
// Skills names, in order, the project's imported skills the task's
// prompts carry (§5.10): resolved and copied onto the task once, at
// creation; nil or empty = none.
Skills []string
}
PlanTask is one task of a plan as its creator names it (§6.1): the title, the spec verbatim (§1.4), the check the runner runs after a done (§5.5), and the class. Name is the short handle the plan's author gives it so others can depend on it, and Deps the names of the tasks of the same plan it waits for (§6.1). Caps and per-task repos arrive with the plan tools of phase 3.
type Probe ¶
type Probe struct {
AgentID int64 `json:"agent_id"`
Adapter string `json:"adapter"`
Harness string `json:"harness"`
Model string `json:"model"`
}
Probe is one agent a runner should probe (§8 "Health"): a failure-broken agent whose next probe is due. The runner runs the agent's cheap CLI probe and reports whether it passed.
type Progress ¶
type Progress struct {
LastProgressAt time.Time
LastActivityAt time.Time
Events []string
// Loop reports the runner's loop watch tripped (§5.6): the worker
// repeats the same tool call. LoopCall names the call. The signal
// routes to the foreman like a stall; the run is not stopped.
Loop bool
LoopCall string
}
Progress is what a runner's heartbeat carries besides its hold on the job (§5.6): when the worker last made progress, the last sign of life, and the last few progress events. The timestamps land on the job's row; the events ride along for the runs the engine will have to report (a stall), whose storage arrives with the history.
type ProjectConfig ¶
type ProjectConfig struct {
// Repo is the canonical identity the file names.
Repo string
// InstructionsFile is the repo-relative path the file names under
// instructions_file, "" when it sets none. The client reads it; the
// text rides beside the file's bytes at import.
InstructionsFile string
// RoleFiles maps a role to the repo-relative path the nested
// instructions map names for it; a role absent is not configured.
// The 1.0 alias instructions_file stays in InstructionsFile.
RoleFiles map[string]string
// SkillsDir is the repo-relative skills directory skills_dir names,
// "" when the key is absent.
SkillsDir string
// Values holds the stored JSON of each setting the file sets.
Values map[string]json.RawMessage
}
ProjectConfig is a parsed, validated schema-1 file: the repo identity it names and the settings it sets, each already in the stored shape.
func ParseProjectConfig ¶
func ParseProjectConfig(content []byte) (ProjectConfig, error)
ParseProjectConfig parses and validates a project.yaml. Anything but schema 1's keys is refused with the key named: an unknown key, an unknown schema version, a value of the wrong type, a repo that is not in canonical form. The YAML is a strict subset (top-level keys with a scalar, a flow list or a block list), the same hand-rolled kind the plan files use, so the engine takes no dependency for it.
type ProjectInfo ¶
type ProjectInfo struct {
ProjectID int64 `json:"project_id"`
RepoURL string `json:"repo_url"`
Harness string `json:"harness"`
Model string `json:"model"`
ActivePlans int `json:"active_plans"`
NeedsYou int `json:"needs_you"`
}
ProjectInfo is one line of the project list: a project's repo URL and foreman, how many plans are active in it and how many items wait on the user (§10).
type ProjectSettings ¶
type ProjectSettings struct {
ProjectID int64 `json:"project_id"`
RepoURL string `json:"repo_url"`
Settings []SettingValue `json:"settings"`
// ImportHash is the hash of the project.yaml content last imported;
// empty when none was.
ImportHash string `json:"import_hash,omitempty"`
}
ProjectSettings is a project's editable settings, every key with its effective value.
type ProjectStats ¶
type ProjectStats struct {
ProjectID int64 `json:"project_id"`
RepoURL string `json:"repo_url"`
Rows []StatsRow `json:"rows"`
}
ProjectStats is one project's stats rows (§8), ordered by class and then best score first: the agent that wins a class leads it.
type PruneReport ¶
type PruneReport struct {
Messages int64 `json:"messages"`
Artifacts int64 `json:"artifacts"`
Files int64 `json:"files"`
}
PruneReport counts what a prune took (§9).
type PushApproveResult ¶
type PushApproveResult struct {
// Released counts the landings moved from waiting to a queued push.
Released int `json:"released"`
}
PushApproveResult is what releasing the push gate did (§6.3): how many waiting landings now have a push job queued.
type Ranked ¶
type Ranked struct {
Agent int64 `json:"agent"`
Weight int `json:"weight"`
Draw float64 `json:"draw"`
}
Ranked is one candidate of an agent pick (§8): its cost weight and the success-rate draw the ranking sorted it by.
type RestartResult ¶
RestartResult is what RestartTask did: the fresh work job it queued on the task's key.
type RetireResult ¶
type RetireResult struct {
Retired []int64 `json:"retired"`
Plans []RetiredPlan `json:"plans"`
}
RetireResult is what Retire did: the task ids it retired, and per plan touched the state the plan reached after the close rule ran.
type RetiredPlan ¶
RetiredPlan is one plan a retirement touched, with its state after it.
type ReviewBranchResult ¶
type ReviewBranchResult struct {
Answer Answer `json:"answer"`
ProjectID int64 `json:"project_id"`
JobID int64 `json:"job_id"`
Run int `json:"run,omitempty"`
// Family is the author's family the reviewer must differ from; empty
// when it could not be told and the default reviewer was taken.
Family string `json:"family,omitempty"`
// Source is where the family came from: job, trailer or default.
// Empty when no witness named one and no default reviewer was
// configured — the open placement prefer makes (issue #126).
Source string `json:"source"`
}
ReviewBranchResult is what ReviewBranch did: the review job created — or the live one a repeat coalesced behind — and who it excludes and why.
type ReviewConfig ¶
type ReviewConfig struct {
Tier string // ReviewTierBelow or ReviewTierSame; ” = below
Effort string // low, medium, high or max; ” = medium
Family string // ReviewFamilyPrefer or ReviewFamilyRequire; ” = prefer
}
ReviewConfig is the engine-wide default for reviews (§7, §8): the config file's review.tier, review.effort and review.family. The zero value is the default: one tier below the task's, at medium effort, another family preferred over the author's own.
type RoleFileRemoval ¶
type RoleFileRemoval struct {
Role string
// Removed is the imported record that was deleted.
Removed *RoleInstruction
// Kept is the local record that kept its text against the dropped
// key, the ErrRoleTextKept case of a role file the file no longer
// names.
Kept *RoleInstruction
}
RoleFileRemoval is one role an import moved to not configured, or one whose local override stood against the move (§5.9): exactly one of Removed and Kept is set.
func (RoleFileRemoval) Change ¶
func (r RoleFileRemoval) Change() ConfigChange
Change is the import-result line of one removal: an applied move to not configured, or the conflict of a local override that kept its text while the file dropped the role's key.
type RoleInstruction ¶
type RoleInstruction struct {
ProjectID int64 `json:"project_id"`
RepoURL string `json:"repo_url"`
Role string `json:"role"`
// State is one of RoleStateNotConfigured, RoleStateEmptyByChoice,
// RoleStateFile.
State string `json:"state"`
// Source is where the text came from: default (not configured),
// generated (seeded from the embedded defaults), local or imported.
Source string `json:"source"`
// Version is the settings version the text is pinned to: the import
// hash for an imported text, the defaults version for a generated
// one, empty for a local text.
Version string `json:"version,omitempty"`
// Provenance is the one-line reading of the source, the way the
// foreman's status shows it: "generated from <version>, by upgrade"
// for a seeded text, the source word otherwise.
Provenance string `json:"provenance"`
Bytes int `json:"bytes"`
Digest string `json:"digest"`
// Text is the effective text: the row's; empty for a role not
// configured or an empty-by-choice row, for at run time the engine
// never falls back to a text in the binary (§5.9).
Text string `json:"text,omitempty"`
}
RoleInstruction is a role's effective text and its provenance, as a read or a set returns it.
type RoleSnapshot ¶
type RoleSnapshot struct {
Text string `json:"text,omitempty"`
Source string `json:"source,omitempty"`
Version string `json:"version,omitempty"`
Digest string `json:"digest,omitempty"`
Bytes int `json:"bytes"`
}
RoleSnapshot is a role text as a task recorded it: the text and its provenance — source, settings version, digest and size.
type RoundCaps ¶
RoundCaps are the per-task caps on feedback rounds and restarts (§7, §6.1). Fix caps the fix rounds a failed check asks for, Review the fix rounds a review's "changes" asks for — both counted as the task's fix jobs — and Restart the restarts (§5.6); the shipped caps are 2, 2 and 3, the third restart being the foreman's once the escalation ladder (§8) has spent two. A task's caps JSON overrides them key by key (fix_rounds, review_rounds, restart).
type SettingValue ¶
type SettingValue struct {
Key string `json:"key"`
Value string `json:"value"`
Default bool `json:"default"`
Source string `json:"source"`
Hash string `json:"hash,omitempty"`
}
SettingValue is one key's effective value: Value is the +-separated (or integer) rendering, Default says no override or import sets it. Source is its provenance; Hash is the hash of the imported file the value came from, set only for an imported value.
type SetupSpec ¶
type SetupSpec struct {
Command string `json:"command"`
CacheKeyFiles []string `json:"cache_key_files,omitempty"`
TimeoutMinutes int `json:"timeout_minutes"`
Source string `json:"source,omitempty"`
Hash string `json:"hash,omitempty"`
}
SetupSpec is the project's setup as a task records it (§5.7): the command, the repo-relative files whose contents key the cache, the time bound, and where the setting came from (its source and the hash of the imported file), so a later change to the project setting leaves a recorded task alone.
type Skill ¶
type Skill struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
// For is the frontmatter's 'for:' — worker, reviewer or both; empty
// on an import means both.
For string `json:"for,omitempty"`
Text string `json:"text"`
Digest string `json:"digest,omitempty"`
TreeDigest string `json:"tree_digest,omitempty"`
}
Skill is one of the project's imported skills.
type SkillFileDigest ¶
SkillFileDigest is a client's current digests of one skill directory: of its SKILL.md and of the whole tree (§5.10), each over the file text without the version header.
type Slot ¶
type Slot struct {
ProjectID int64 `json:"project_id"`
State SlotState `json:"state"`
HolderID int64 `json:"holder_id,omitempty"`
Label string `json:"label,omitempty"`
Since string `json:"since,omitempty"`
Events []string `json:"events,omitempty"`
}
Slot is the foreman slot of one project and who holds it (§4.6): the holder's id (attach hold id, or job id for a turn), its label, since when and, for a turn, the events it carries.
type SlotState ¶
type SlotState string
Slot states (§4.6): the key's live slot has one holder at a time, an attached chat or a headless turn.
type StatsRow ¶
type StatsRow struct {
AgentID int64 `json:"agent_id"`
Harness string `json:"harness"`
Model string `json:"model"`
Tier string `json:"tier"`
Effort string `json:"effort,omitempty"` // the agent's effort level; outcomes still group by harness/model
Account string `json:"account"`
Runner string `json:"runner"`
Live bool `json:"live"` // the runner was seen within the stats window
Class string `json:"class"`
Skills int `json:"skills"` // how many skills the tasks of the row carried (§5.10); rows with different counts are never pooled
Attempts int `json:"attempts"`
Successes int `json:"successes"`
Score float64 `json:"score"`
InputTokens *int64 `json:"input_tokens"`
OutputTokens *int64 `json:"output_tokens"`
CachedInputTokens *int64 `json:"cached_input_tokens"`
ReasoningTokens *int64 `json:"reasoning_tokens"`
Tokens *int64 `json:"tokens"`
CostUSD *float64 `json:"cost_usd"`
WallSeconds *float64 `json:"wall_seconds"`
ModelCalls *int64 `json:"model_calls"`
CallsPerJob *float64 `json:"calls_per_job"`
CallsPerSuccess *float64 `json:"calls_per_success"`
CostPerSuccess *float64 `json:"cost_per_success"`
CostPerSuccessUnit string `json:"cost_per_success_unit,omitempty"`
}
StatsRow is one (agent, class) line of a project's stats (§8): which agent, on which runner, how often it took the class in this repo and how often that succeeded, with the score the dispatcher ranks by. Attempts is also the confidence in the score: the more attempts, the less the prior pulls. An agent that offers the class and has no outcome row yet carries attempts 0 and the bare prior score, so a cold start shows instead of staying blank.
type StatusInfo ¶
type StatusInfo struct {
Pid int64 `json:"pid"`
StartedAt string `json:"started_at"`
Jobs map[string]int `json:"jobs"`
Running []StatusJob `json:"running"`
// RunnerDraining is true while a runner has a restart drain pending
// (§5.1): it finishes its running jobs and exits, and the status
// renders the restart that waits behind them.
RunnerDraining bool `json:"runner_draining,omitempty"`
// ForemanMessages counts the user's messages waiting in the foreman
// inbox (§4.2 item 3): queued while a chat is attached, a turn runs,
// or the wake cannot happen; delivered ones are not counted.
ForemanMessages int `json:"foreman_messages"`
// Slots is who holds the foreman's live slot, per project (§4.6):
// free, a headless turn or an attach, with the holder's label and
// since when.
Slots []Slot `json:"slots"`
Plans []StatusPlan `json:"plans"`
// HiddenFinishedPlans counts the finished plans — done or
// cancelled — the plans list left out (§10): the default keeps the
// newest statusFinishedKeep, the all view lists every one. The
// status views render it as the one summary line that says the
// rest exist.
HiddenFinishedPlans int `json:"hidden_finished_plans,omitempty"`
Report *StatusReport `json:"report,omitempty"`
NeedsYou []NeedsYouItem `json:"needs_you"`
// ForemanHolds lists the needs-you entries the foreman holds (§10),
// split out of NeedsYou for a caller that is not the foreman: the
// engine's own Status keeps every entry in NeedsYou, and the API
// moves the held ones here (SplitForemanHolds).
ForemanHolds []NeedsYouItem `json:"foreman_holds,omitempty"`
// Cooling lists the agents held back for quota (§8, §10): shown
// beside the running jobs, never in the needs-you queue.
Cooling []CoolingAgent `json:"cooling,omitempty"`
// Held lists the queued jobs a full cap layer holds (§8.1); the
// runner layer's are jobs waiting for a runner slot.
Held []HeldJob `json:"held,omitempty"`
// Retries lists the queued jobs waiting out an infrastructure
// backoff (§5.1): the job's not-before and the failure's words.
Retries []InfraRetry `json:"retries,omitempty"`
// Instructions lists the projects in scope that have project
// instructions (§5.9): source, version, size and digest; the text only
// from StatusWithInstructions.
Instructions []StatusInstructions `json:"instructions,omitempty"`
RoleInstructions []StatusRoleInstructions `json:"role_instructions,omitempty"`
Skills []StatusSkill `json:"skills,omitempty"`
}
StatusInfo is the engine's own state as the API shows it (§13): which process is the engine, since when, the jobs by status — and the plans (§6.1, §6.2) with the latest foreman report (§3, §10) and the "needs you" queue (§10): the tasks parked for a person and the plans waiting for approval. The pid and start are what the lifecycle recorded (RecordInstance); a store no engine of this binary has recorded shows zero values.
func (*StatusInfo) CompareInstructionFileDigests ¶
func (s *StatusInfo) CompareInstructionFileDigests(project int64, digests map[string]string)
CompareInstructionFileDigests applies file digests supplied by a client to a single project's status. Missing digests and non-imported roles remain unknown.
func (*StatusInfo) CompareSkillFileDigests ¶
func (s *StatusInfo) CompareSkillFileDigests(project int64, digests map[string]SkillFileDigest)
CompareSkillFileDigests applies skill digests supplied by a client to a single project's status. A skill the client sent no digest for stays unknown; the stored snapshot is never touched.
func (*StatusInfo) SplitForemanHolds ¶ added in v1.1.0
func (s *StatusInfo) SplitForemanHolds()
SplitForemanHolds moves the held entries out of NeedsYou into ForemanHolds (§10): what a caller other than the foreman reads, since the queue holds only what a rule or the foreman gave the user.
type StatusInstructions ¶
type StatusInstructions struct {
ProjectID int64 `json:"project_id"`
RepoURL string `json:"repo_url"`
Source string `json:"source"`
// Version is the hash of the import the text came from; empty for a
// local override.
Version string `json:"version,omitempty"`
Bytes int `json:"bytes"`
Digest string `json:"digest"`
Text string `json:"text,omitempty"`
}
StatusInstructions is how a project's instructions show in status (§5.9): the source, version, size and digest, and the full text only when the caller asked for it.
type StatusJob ¶
type StatusJob struct {
ID int64 `json:"id"`
TaskID int64 `json:"task_id,omitempty"`
Kind string `json:"kind"`
Status string `json:"status"`
Harness string `json:"harness,omitempty"`
Model string `json:"model,omitempty"`
Effort string `json:"effort,omitempty"` // the agent's effort level; shown as model@effort
LeasedAt string `json:"leased_at,omitempty"`
LastEvent string `json:"last_event,omitempty"`
}
StatusJob is one job a runner holds right now (§10): which job, for which task, of what kind, on which agent, since when, and the worker's latest progress event — what it is doing, in one line. A foreman turn has no task; an agent-less runner's job names no agent.
type StatusPlan ¶
type StatusPlan struct {
ID int64 `json:"id"`
RepoURL string `json:"repo_url"`
Title string `json:"title"`
State string `json:"state"`
// ReviewRound, ReviewVerdict and ReviewFindings are the plan review's
// state (§6.2): the round under way while the plan reviews, and
// the latest verdict and findings once a round has ended. The
// findings are the event's whole text; the short views that render
// them cut at their own bound and point to ReviewJob's history.
ReviewRound int `json:"review_round,omitempty"`
ReviewVerdict string `json:"review_verdict,omitempty"`
ReviewFindings string `json:"review_findings,omitempty"`
ReviewJob int64 `json:"review_job,omitempty"`
// ReviewPlacement is the leased plan review's placement kind (issue
// #126) when it is not cross_family: same_family, same_agent or
// unknown. Empty for cross_family (the default, left unsaid), for a
// plan not under review and for a review no reviewer has leased yet.
ReviewPlacement string `json:"review_placement,omitempty"`
// WaitingAmendment is true while the plan reviews with waiting
// amendment work (§6.2): tasks an amendment added or respecified
// under the running round, pending its verdict, which queues the
// next round over them.
WaitingAmendment bool `json:"waiting_amendment,omitempty"`
Tasks []StatusTask `json:"tasks"`
}
StatusPlan is one plan the status lists (§6.1, §6.2, §10): every live plan — active, reviewing, parked for the foreman, or waiting for the user's approval — and the newest finished ones, done or cancelled. The repo URL names whose plan it is; the tasks are the plan's, each with the state and branch the graph is in.
type StatusReport ¶
type StatusReport struct {
ProjectID int64 `json:"project_id"`
JobID int64 `json:"job_id"`
CreatedAt string `json:"created_at"`
Text string `json:"text"`
}
StatusReport is the latest foreman report the status shows (§3, §10): the final message of the project's last done foreman turn — when it ended, and the first lines of what it said. A cut Text ends with `… (full: brigada history --job N)`, naming the job whose history holds the whole report (§9). The foreman token's scope is one project; a caller reading every project sees the latest report among them, the project named by its id.
type StatusRoleInstructions ¶
type StatusRoleInstructions struct {
ProjectID int64 `json:"project_id"`
RepoURL string `json:"repo_url"`
Role string `json:"role"`
State string `json:"state"`
Source string `json:"source,omitempty"`
Bytes int `json:"bytes"`
Digest string `json:"digest,omitempty"`
GeneratedFrom string `json:"generated_from,omitempty"`
Edited *bool `json:"edited"`
ChangedSinceImport *bool `json:"changed_since_import"`
Text string `json:"text,omitempty"`
}
StatusRoleInstructions describes one effective role text. A nil comparison means its baseline or the client's current file digest is unknown.
type StatusSkill ¶
type StatusSkill struct {
ProjectID int64 `json:"project_id"`
Name string `json:"name"`
Description string `json:"description"`
Digest string `json:"digest"`
TimesUsed int `json:"times_used"`
TreeDigest string `json:"tree_digest,omitempty"`
// ChangedSinceImport compares the client's current file and tree
// digests with the imported ones; nil when the client sent none.
ChangedSinceImport *bool `json:"changed_since_import"`
}
type StatusTask ¶
type StatusTask struct {
Tier string `json:"tier,omitempty"`
EffectiveTier string `json:"effective_tier,omitempty"`
Effort string `json:"effort,omitempty"`
// EffectiveEffort is the effort the escalation ladder runs the task
// at (§8); it equals Effort until an effort step.
EffectiveEffort string `json:"effective_effort,omitempty"`
// ReviewTier and ReviewEffort are the task's own tier and effort for its
// reviews (§7, §8); empty = the engine default (review.tier,
// review.effort), which status leaves unsaid.
ReviewTier string `json:"review_tier,omitempty"`
ReviewEffort string `json:"review_effort,omitempty"`
// ReviewPlacement is the placement kind (issue #126) of the task's
// latest review job while the task reviews, when that job was leased
// and the kind is not cross_family: same_family, same_agent or
// unknown. Empty otherwise: a review no reviewer has leased names
// its unservable reason, never a placement.
ReviewPlacement string `json:"review_placement,omitempty"`
// Caps are the task's own round-cap overrides (§6.1); absent keys run
// at the engine default.
Caps map[string]int `json:"caps,omitempty"`
ID int64 `json:"id"`
// Name is the task's plan name (§6.1), empty when it has none: the
// short handle the plan tools name tasks by, shown so a tool's
// caller can carry it back.
Name string `json:"name,omitempty"`
Title string `json:"title"`
Class string `json:"class"`
State string `json:"state"`
Branch string `json:"branch"` // empty while the task has no result branch yet
// Landed is true when the task's landing is recorded and not waiting
// at the push gate (§7.1): the land job squashed it, or a Brigada-Task
// trailer on the default branch was detected and recorded.
Landed bool `json:"landed,omitempty"`
// LandedCommit is the commit the task landed as on its target
// (§7.1), from its landing record; empty when it has none.
LandedCommit string `json:"landed_commit,omitempty"`
// LandReady is the land_ready gate's target state for a done task
// (issue #87): "ready" when its land job is queued or running,
// "held by rule land_ready: <line>" when the gate's exit 1 held the
// landing, and "waiting (retry at <time>, last exit <n>)" when the
// gate answered not ready and the landing is requeued on the backoff.
// Empty when the task is not done, not implement, already landed, or
// held by another rule.
LandReady string `json:"land_ready,omitempty"`
// WaitingOn names the unfinished dependencies of a task that waits
// on them (§6.1): each by its plan name, or "#id" when unnamed.
WaitingOn []string `json:"waiting_on,omitempty"`
// Continues names the dependency whose author's session the task's
// first work job resumes (§6.1), by plan name or "#id"; empty when
// the task continues none.
Continues string `json:"continues,omitempty"`
// Skills names the skills the task recorded (§5.10), in order.
Skills []string `json:"skills,omitempty"`
}
StatusTask is one task of a status plan (§6.1).
func (StatusTask) LandedLabel ¶
func (t StatusTask) LandedLabel() string
LandedLabel words the task's landing state for the per-task lines the foreman reads — the status views and the digest's plan lines (§7.1, §10): `landed@<sha>` for a done implement task whose landing is recorded, `not landed` for a done implement task without one, "" for everything else — a task of a class that never lands, or one not done yet. A done task that has not landed must read differently from a landed one.
type SubmitParams ¶
type SubmitParams struct {
// Key is the queue: 'task:<id>' for worker jobs, 'foreman:<project>'
// for the foreman's turns. At most one live job exists per key.
Key string
ProjectID int64
TaskID int64 // 0 for a foreman turn
Kind Kind
Mode Mode
Prompt string
// Harness and Labels are placement constraints: the harness that
// must run the job (” = any) and the labels a runner must offer in
// full (§8).
Harness string
Labels []string
// Model is the model a foreman turn must run on, beside its harness:
// only an agent offering exactly that pair may lease the turn. Empty on
// every other job, and on a turn submitted without one, which follows
// the project's own choice (foremanPairTx).
Model string
// ExcludeFamily is the family rule's exclusion (§7, §8): when set,
// only an agent of another family that offers the review class may
// lease the job. The engine sets it on a task's review job, from
// the family of the author job's agent.
ExcludeFamily string
// ExcludeAgent is the retry rule's exclusion (§3, §7): when set,
// that one agent may not lease the job. Work jobs have no family
// rule; review retries also apply the review family and class rules.
ExcludeAgent int64
// RequireAgent is a placement pin (§3): when set, the one agent
// the job must run on — the author's own, read from the latest work
// or fix job — because the harness alone does not name it: another
// runner may offer the same harness, and the author's runner may
// hold a second agent of it. An automatic restart pins a different
// eligible agent instead. A runner not offering the agent cannot
// lease the job at all.
RequireAgent int64
// Round names the task's rounds budget this job spends (§7, §6.1):
// roundFix for a fix round a failed check asked for, roundReview
// for one a review's changes asked for — both counted as the task's
// fix jobs — and roundRestart for a restart. Empty spends nothing
// (a work job, a review job: §7 caps the review loop's fix rounds,
// not the reviews themselves) and lets a plain fix submit spend a
// fix round. RoundNone spends nothing even for a fix job: the
// foreman's answer resumes the author's session without a round
// (§3), so its job must clear the caps and the counts both.
Round string
// Restart marks the job as a restart round (§5.6): any live job on
// the key is cancelled first, and the job counts toward the task's
// restart cap.
Restart bool
// Branch is the branch a standalone review (no task) reads: the one
// job whose branch the submit names, since no task row holds it. The
// engine names the branch of every other job at its first lease.
Branch string
// NotBefore is the time before which the queued job may not be
// leased (§3, §4.2): the engine's retry and re-wake delays write it
// so a job does not fire again while the network is still down. The
// zero value means the job is leasable at once.
NotBefore time.Time
// SkipLandReady marks the land job an approval queues to release the
// land_ready gate's hold (issue #87): the runner runs no command and
// lands. False on every other job.
SkipLandReady bool
// PromptMeta is what the job records beside its composed prompt
// (§5.9 'Versioning'): the template's version and the digests of
// the instruction and skill texts the prompt carried, so a
// recorded prompt is reproducible. ” on a job whose prompt was
// composed without guidance.
PromptMeta string
}
SubmitParams describes the job to create for a key (§12.1).
type SubmitResult ¶
type SubmitResult struct {
Answer Answer
// JobID is the created job, or the live job a pending submit
// coalesced behind.
JobID int64
// Run is the key's run number when a job was created: how many jobs
// the key has had, this one included.
Run int
}
SubmitResult is what Submit did.
type SweepReport ¶
type SweepReport struct {
Expired []int64 // leases that lapsed; back to queued, keeping their lease ids
HardCapped []int64 // live jobs failed past their kind's hard cap
QueueTimedOut []int64 // queued jobs that waited past the queue TTL without a lease
}
SweepReport names the jobs the sweep dealt with, so a caller (and a test) can tell an expiry that opens a revive window from a failure.
type TaskInfo ¶
type TaskInfo struct {
Tier string `json:"tier,omitempty"`
EffectiveTier string `json:"effective_tier,omitempty"`
Effort string `json:"effort,omitempty"`
ID int64 `json:"id"`
ProjectID int64 `json:"project_id"`
PlanID int64 `json:"plan_id"`
Title string `json:"title"`
State string `json:"state"`
RepoURL string `json:"repo_url"`
BaseRef string `json:"base_ref"`
Branch string `json:"branch,omitempty"` // empty until the task has a result branch
// LatestJobID is the task's newest job of any kind; 0 before the
// first one was queued.
LatestJobID int64 `json:"latest_job_id,omitempty"`
}
TaskInfo is a task as a client reads it (§12.1): where its change lives — the repo, the base it started from, its current result branch (the latest attempt's, -r<n> included) — and the job a reader of its record starts from. The engine answers which refs; it never runs git on a client's behalf (product §5 rule 1).
type TaskInstructions ¶
type TaskInstructions struct {
Worker RoleSnapshot `json:"worker"`
Reviewer RoleSnapshot `json:"reviewer"`
Skills []TaskSkill `json:"skills,omitempty"`
// SkillsDir is the project's effective skills_dir when the task was
// resolved; the directory line of every prompt of the task names it.
SkillsDir string `json:"skills_dir,omitempty"`
}
TaskInstructions is the snapshot a task takes at creation of what its prompts carry: the worker and reviewer texts and the ordered skills.
func (TaskInstructions) ReviewerBytes ¶
func (ti TaskInstructions) ReviewerBytes() int
ReviewerBytes is what the reviewer prompt carries of instructions plus skills: the reviewer text, the worker text as the convention the author followed, and every skill not meant for the worker alone.
func (TaskInstructions) WorkerBytes ¶
func (ti TaskInstructions) WorkerBytes() int
WorkerBytes is what the worker prompt carries of instructions plus skills: the worker text and every skill not meant for the reviewer alone.
type TaskLandInfo ¶
type TaskLandInfo struct {
ID int64 `json:"id"`
Title string `json:"title"`
Class string `json:"class"`
State string `json:"state"`
Spec string `json:"spec"`
// Deps are the ids of the tasks this one depends on (§6.1).
Deps []int64 `json:"deps,omitempty"`
// Approved: the task is done and its last review, after its last
// author job, was a substantive approve (§7).
Approved bool `json:"approved"`
// ApprovedHead is the pushed head the approving review saw, as the
// author job's result recorded it; empty when it recorded none, and
// land then warns instead of refusing (§7.1).
ApprovedHead string `json:"approved_head,omitempty"`
// Summary is the worker's summary (§5.4) from the task's last author
// job: model output, data only.
Summary string `json:"summary,omitempty"`
// CommitMessage is the commit message (§5.4) the task's last author
// job's worker wrote for the land job (§7.1): subject and body in the
// repo's own style, model output, data only. Empty when the worker
// gave none, and land then builds the message itself.
CommitMessage string `json:"commit_message,omitempty"`
// Family and Model are the authoring agent's, from the job record
// (§7); empty when the job has no agent or the agent is gone.
Family string `json:"family,omitempty"`
Model string `json:"model,omitempty"`
}
TaskLandInfo is what `brigada land` needs of a task beyond TaskInfo (§7.1): the data the commit message is built from, the task's dependencies, and whether its last review approved it. The client builds the message and runs every git command; the engine runs none.
type TaskParams ¶
type TaskParams struct {
RepoURL string
BaseRef string
Title string
Spec string
CheckCmd string // ” = no check
Class string // ” = implement
// Harness and Labels are the queued job's placement constraints
// (§8): the harness that must run it and the labels a runner must
// offer in full.
Harness string
Labels []string
// Skills names, in order, the project's imported skills the task's
// prompts carry (§5.10): resolved and copied onto the task once, at
// creation; nil or empty = none.
Skills []string
}
TaskParams is one task submitted whole (§12.1: tasks, create): the debug submit path until the foreman's plan tools exist — one repo, one base ref, the spec verbatim, and the check the runner runs after a done (§5.5). The worker prompt is not the caller's to bring: the engine composes it from the spec (§1.4, §5.3).
type TaskResult ¶
TaskResult is what SubmitTask did: the task it created, the project it found or made for the repo, and the job submit's own answer.
type TaskSkill ¶
type TaskSkill struct {
Name string `json:"name"`
For string `json:"for"`
Text string `json:"text"`
Digest string `json:"digest"`
// Description is the skill's frontmatter line, recorded beside the
// text so a plan review can judge the pick without the store.
Description string `json:"description,omitempty"`
TreeDigest string `json:"tree_digest,omitempty"`
}
TaskSkill is one skill as a task recorded it, in the order given.
type WakeBudgetInfo ¶
type WakeBudgetInfo struct {
ProjectID int64 `json:"project_id"`
RepoURL string `json:"repo_url"`
Budget int `json:"budget"`
Wakes int `json:"wakes"`
ResetsAt string `json:"resets_at"`
Waiting int `json:"waiting"`
// Default says no per-project setting stands: the engine's default
// (the config file's foreman_wakes_per_day, else the built-in 20)
// is the budget in effect.
Default bool `json:"default"`
}
WakeBudgetInfo is one project's foreman wake budget (§4.3) as `brigada budget` shows it: the effective daily budget, today's use since UTC midnight, the window's end and how many inbox rows wait.
type WakeOutcome ¶
type WakeOutcome string
WakeOutcome is what one wake evaluation did (§4.2): woke a turn, or why the pending inbox stays pending. `brigada ask` reports it, so a user message never queues in silence (§4.2 item 3).
const ( WakeWoken WakeOutcome = "woken" // a foreman turn was submitted carrying the inbox WakeNothing WakeOutcome = "nothing_pending" // the inbox was empty WakeAttached WakeOutcome = "attached" // a person is attached; the session reads the inbox through events WakeTurnRunning WakeOutcome = "turn_running" // a turn is live; its completion re-evaluates WakeNoForeman WakeOutcome = "no_foreman" // the project names no foreman harness WakeStalled WakeOutcome = "stalled" // failing turns carried the inbox too often WakeBudgetSpent WakeOutcome = "budget_spent" // the day's wake budget is spent (§4.3) WakeDuplicate WakeOutcome = "duplicate" // a user message repeating one sent moments ago (§12.1); nothing new was queued WakeLimited WakeOutcome = "limited" // the foreman hit its subscription limit and has not reset (§4.4) )
Source Files
¶
- answer.go
- approval.go
- approval_stop.go
- archive.go
- artifacts.go
- ask.go
- askuser.go
- attach.go
- basefetch.go
- basestale.go
- budget.go
- busy.go
- calls.go
- canon.go
- canonrewrite.go
- capacity.go
- caps.go
- continue.go
- cooling.go
- doc.go
- engine.go
- events.go
- extrarepos.go
- foreman.go
- foremanhold.go
- foremanpair.go
- health.go
- history.go
- instructions.go
- issues.go
- job.go
- land.go
- landdetect.go
- landretry.go
- lease.go
- leasetoken.go
- limit.go
- loop.go
- noreviewer.go
- planamend.go
- planblock.go
- planbudget.go
- planmirror.go
- planrecover.go
- planreopen.go
- planreview.go
- plans.go
- projectconfig.go
- projectconfig_edit.go
- projectimport.go
- projects.go
- rereview.go
- review.go
- reviewfamily.go
- reviewparked.go
- reviewtier.go
- roleinstructions.go
- rotate.go
- runner.go
- runnerslot.go
- sample.go
- settings.go
- setup.go
- skills.go
- slot.go
- snapshot.go
- stats.go
- status.go
- submit.go
- sweep.go
- taskcaps.go
- taskland.go
- taskretire.go
- tasks.go
- taskskills.go
- taskstate.go
- taskview.go
- view.go
- wake.go