run

package
v0.7.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 5, 2026 License: Apache-2.0 Imports: 24 Imported by: 0

Documentation

Overview

Package run is the run: one supervisor that launches every ready task of a plan store and owns the lifecycle from first dispatch to the root's completion. The chat and the store's other writers are not part of this package; steering reaches a run only through the store, as one more writer among the workers.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func LandingNote

func LandingNote(landing Landing) string

LandingNote is [landingNote] as a door outside this package reads it: the one sentence a landing answers with, so a headless door can carry the branch the landing named out to its caller in the same words the run's own page holds.

func SeatFor

func SeatFor(role string) string

SeatFor is the role-to-tier table: the crew row a task's role rides.

The run's root and every coordinator are planning work and take the mastermind row; a leaf that does the work itself takes the worker row, which is also where a task born from add or split sits; the review round reads a finished leaf against its acceptance and takes the careful work tier; and a probe is a small disposable unknown and takes the small-work row. Any other word, a role this build has not learned, or a task the store could not name — does the work, so an unknown word falls to the seat every task is born in rather than failing a task on its metadata.

func SignWork

func SignWork(before session.RunTreeSnapshot) (int, error)

SignWork signs an in-place run's worker commits using the landing's model choice. The run door itself makes no commit in the person's folder.

func Start

func Start(ctx context.Context, spec Spec) (Outcome, Summary)

Start is the one door a caller runs a plan through: it puts the run's words on the store's root task, runs the supervisor over the store to one outcome word, and answers what came of it. The context is the run's wall — workers end with it — and nothing here needs a worker of its own: every seat, the root's included, comes from the spec's factory.

IT DOES NOT ANSWER WHILE A WORKER IT STARTED IS ALIVE: the supervisor drains every goroutine it launched before its outcome comes back here, so a caller may close its store — or the process — on the line after this returns and nothing of the run is left to write into it.

func StepsPerTask

func StepsPerTask(ctx context.Context) int

StepsPerTask answers the cap carried by a context the supervisor built, and zero when there is none — zero being the run's word for "no cap".

func WakeClause

func WakeClause(ctx context.Context) string

WakeClause answers the resume clause carried by a context the supervisor built for a wake, and "" for every other worker — the ordinary launch, whose opening carries the trajectory's own resume sentence instead.

func WithSpendBank

func WithSpendBank(ctx context.Context, bank func(float64)) context.Context

WithSpendBank returns a context that reports cumulative spend as a worker banks calls. Workers without this property remain valid and report at return.

func WithStepsPerTask

func WithStepsPerTask(ctx context.Context, steps int) context.Context

WithStepsPerTask returns a context that carries the cap a worker should hold itself to. The supervisor wraps every worker's context with it; a worker that ignores it is uncapped, not broken.

func WithWakeClause

func WithWakeClause(ctx context.Context, clause string) context.Context

WithWakeClause returns a context carrying the resume clause a woken parent's worker opens with — the clause naming every child that landed and what to do with them (internal/run's supervisor composes it). A worker that ignores it is one no wake reached, the way an empty clause is no wake at all.

func WorkSeat

func WorkSeat(profileDir, work string) string

WorkSeat is the model this run's own work seat holds: the door's work seat where it named one, the profile's worker row otherwise — exactly the seat a leaf of the run is built on (CrewFactory). A delegated program's model API answers on it whatever the program asks for that nothing here can reach.

Types

type AdmissionGate

type AdmissionGate interface {
	// MayStart answers whether one more worker fits the current machine reading.
	MayStart() bool
	// Started counts a worker only after its factory has returned.
	Started()
	// Returned gives that worker's lane back on every return road.
	Returned()
}

AdmissionGate is the machine's answer at each worker start and the shared count of workers it has admitted. A nil gate admits every start. The gate stays outside the store lock because its host reading may take time.

type BashWorker

type BashWorker struct {
	// contains filtered or unexported fields
}

BashWorker implements Worker by hosting one session agent on the bash belt for one store task. The agent is built the way the session builds a bash-belt task worker today (session.NewBeltWorker, with the task's own id as the agent name — the supervisor's claim trick, so the ownership check the finish command answers is taken against the exact name the task was claimed with); its turn loop runs until the agent ends its turn or the step cap on the context is reached, and then a Report comes back.

EVERY STEP IS RECORDED, and the trajectory is the record — nothing in the worker's transcript is. A step line is appended when the step finishes, and the worker's own ending is one more line, so a person opening the task's folder reads the run the way it happened and how it ended.

THE RESUME CLAUSE IS THE ONLY THING PORTED OF CHECKPOINT REPLAY. A worker opened on a task whose trajectory already has steps opens with the sentence the fresh worker needs — a predecessor was interrupted mid-work, and the effects it left are unannounced facts about the tree — and never replays a recorded command: the file is a record of what ran, not a checkpoint of what to run again.

THE TASK'S SPEND ROW IS WRITTEN HERE, on the model the seat was built on ([recordSpend]): the run worker's session has no graph node to charge through the ordinary plan-spend path, so this is the one writer of a run task's row, and the model it names is the model every call the worker made went out on.

THE FLAG IS THE DOOR'S. The bash belt is what makes a run wire this worker at all; the seat's constructor reads the switch once and refuses when CODEAF_TASK_BELT names the older belt, so on that road not one byte of any prompt, belt or landing changes — nothing constructs this worker.

func NewBashWorker

func NewBashWorker(store *plandb.Store, workspace, model, standing string, completer session.Completer) *BashWorker

NewBashWorker builds the seat the run's factory hands each claimed task to. The store is the run's own — the trajectory is appended beside it and the plandb shim is armed against it — the workspace is the run's one working copy every worker of the run shares, and the model is the seat's. The completer is the seat's provider: a test scripts it, a run hands the door's own.

func (*BashWorker) Run

func (w *BashWorker) Run(ctx context.Context, task plandb.Task) (rep Report, runErr error)

Run hosts one agent's turn loop for the task until the agent ends its turn or the step cap on the context is reached, and returns a Report either way. A turn that ended cleanly reports the agent's own account of the work; a loop the cap stopped reports the steps it took and an error, because a task that ran out of steps did not finish; and a wall or a provider ending the turn ends the task with that reason.

type DelegateSetup

type DelegateSetup struct {
	// Exe is codeaf's own executable, which the program runs as. Empty is this
	// process's own; a test names a script that speaks the records.
	Exe string
	// Grace overrides the launch's SIGTERM grace, for a test.
	Grace time.Duration
	// CompleterFor answers the funnel a call on a model goes out through: the
	// conversation's own completer (session.RunSpec.CompleterFor), so a
	// program's calls take the road the conversation's own do. Nil is a run
	// with no model road, whose API answers every call with that sentence.
	CompleterFor func(model string) session.Completer
	// Serves answers whether this conversation's services can take a call on a
	// model (session.RunSpec.Serves); nil answers yes for every model.
	Serves func(model string) bool
	// ModelPrice is the known per-token price for reserving model API calls.
	ModelPrice func(model string) (input, output float64, known bool)
	// Seat is the run's own work seat ([WorkSeat]): the model a call is
	// answered on when the one the program asked for cannot be reached here.
	Seat string
	// Ledger is the spending ledger the calls are written to. Empty is this
	// machine's own (session.UsageLedgerPath); a test names a file of its own.
	Ledger string
	// Keepalive overrides the model API's keepalive interval, for a test.
	Keepalive time.Duration
	// AuthKeySource names the safe credential source for the served model,
	// so a connected provider never borrows the default provider's explanation.
	AuthKeySource func(model string) string
	// PlainFolder says the program works in its folder without git
	// (session.RunSpec.PlainFolder), so the program's line carries its own
	// flags for that (delegate.Delegate.PlainFolder).
	PlainFolder bool
	// IgnoredFile is the run's start-time ignore list, and InputsFile the
	// untracked files copied into its copy with their fingerprints
	// (session.ProgramFolder.InputsFile). Both are passed to the child, whose
	// recorder keeps both out of every tree it records.
	IgnoredFile string
	InputsFile  string
	// BriefNote is the line the program's brief opens with when it works in a
	// copy of the person's repository (session.ProgramFolder.BriefNote): where
	// the copy is. Empty for a folder worked in itself.
	BriefNote string
	// Hold is the file the run's hold on the program's folder is taken on
	// (session.ProgramFolder.Hold), handed to the program's process so the
	// folder stays held until it has gone ([delegate.HoldEnv]). Nil hands none.
	Hold *os.File
	// Crew is the conversation's crew (session.RunSpec.Crew), which the
	// program's line carries in its own flags (delegate.Delegate.CrewFlags) so
	// it works on the models the person chose. Zero leaves it to its own.
	Crew delegate.Crew
	// Conversation is the id of the conversation the run belongs to
	// (session.RunSpec.Conversation), which every ledger row the program's
	// calls write names as its Root and its Session, beside the task's id, so
	// the conversation's spend and the spending page can say whose money it
	// was. Empty leaves the rows naming no conversation.
	Conversation string
	// OnCharge is told every priced call as it is metered
	// (session.RunSpec.OnCharge), for the conversation to fold the call's
	// tokens, model and dollars into its own books. Nil tells nobody.
	OnCharge func(session.RunCharge)
}

DelegateSetup is how a delegated run starts its program's process and serves it models.

type DelegateWorker

type DelegateWorker struct {
	// contains filtered or unexported fields
}

DelegateWorker runs one program as the worker of one task.

func NewDelegateWorker

func NewDelegateWorker(store *plandb.Store, workspace string, program delegate.Delegate, setup DelegateSetup, cost float64, elapsed time.Duration) *DelegateWorker

NewDelegateWorker builds the worker. cost and elapsed are the run's ceilings, zero for none.

func (*DelegateWorker) Run

func (w *DelegateWorker) Run(ctx context.Context, task plandb.Task) (Report, error)

Run starts the program and reads it to its ending. The Report's Result is the ending in words a person reads; Steps is what the program said it did; USD is what the model API metered, and nothing the program said about it.

type Landing

type Landing struct {
	Branch  string
	Changed []string
	Refused string
}

Landing is what a run's landing answers: the branch the working copy's work was committed on, the paths that commit carried, and — when the landing refused — the sentence saying why. A landing either names a branch or says what stopped it, so the two are never both empty and never both set.

func Land

func Land(ctx context.Context, store *plandb.Store, workspace, base, rootID string) (Landing, error)

Land commits a run's working copy onto its branch and writes the answer as a note on the root task, so the run's own page carries where its work went.

THE WORK IS THE COPY'S OWN (session.LandRunTree), because a run's workers edit through bash and leave no ledger: the tree's status and commits since the run's base are the record. The commit message is the root task's title — the run's own name for the thing the person asked for.

A REFUSAL IS AN ANSWER, NOT A FAULT. Nothing to land is the ordinary ending of a run that only read, and it is written on the root the same way a landing is. An error is the run having no working copy to land in at all, and then there is no note to write, because there is nothing about this run to say.

type Limit

type Limit string

Limit is which bound a person set ended a run that reached it. The outcome word above is one sentence for both limits and the exit ladder keeps its one rung, so this fact is what says which limit fired, and it is carried beside the word rather than read out of it: set where the run decides the limit was reached ([Supervisor.limitHit]), read where the ending is drawn.

const (
	// LimitTime is the elapsed limit.
	LimitTime Limit = "time"
	// LimitCost is the spend ceiling.
	LimitCost Limit = "cost"
)

type Limits

type Limits struct {
	// CostUSD is what the whole run may spend. Banked calls count while a worker
	// is still working and reconcile with its final Report. When the counter has
	// reached it no new worker starts, work in flight ends, and the run ends on
	// the limit word of the outcome ladder.
	CostUSD float64
	// Elapsed is how long the whole run may remain active. When it passes,
	// workers already in flight are ended and drained, no new worker starts,
	// and the run ends on the same limit word as the cost counter.
	Elapsed time.Duration
	// StepsPerTask is handed to every worker through its context, so the loop
	// a worker hosts can cap itself without the supervisor counting its steps.
	StepsPerTask int
	// StaleAfter is how long a claim may go untouched before a pass takes it
	// over: a claimed task whose owning process has not been seen for this
	// long is released so the ready set offers it again. It is a field here
	// rather than an environment variable because it bounds how long a run
	// waits on a process that may have died, and zero takes the default
	// (defaultStaleAfter) rather than meaning "no stale claim ever".
	StaleAfter time.Duration
	// ReviewRound turns the review round on. When it is set, a work-seat leaf
	// that lands done spawns one check task under its parent — whether the leaf's
	// own `plandb done` wrote the ending or this run did — and a check whose
	// result begins "does not hold" leaves its sentence as a note on the leaf it
	// read AND adds a `fix:` task under that leaf's parent which the run waits on.
	// A `fix:` task is checked in turn, but a finding on one is a note and no
	// second fix task, so a run cannot loop. It is a bool defaulting false so
	// every caller that does not ask for it keeps the run it had — no check
	// tasks, nothing new on the plan.
	ReviewRound bool
}

Limits bound a run from the outside. Every field is optional: a CostUSD or Elapsed of zero (or less) sets no corresponding run limit, a StepsPerTask of zero hands the worker no cap, and ReviewRound's false is the run every caller had before it.

type Outcome

type Outcome string

The outcome words are the exit ladder's own (cmd/codeaf/envelope.go, the Short column every headless verb prints beside its exit codes), so a caller of this package and a reader of the process's exit code say the same thing about the same ending. The fifth rung — needed an answer — has no home here yet: nothing in the loop asks a question.

const (
	OutcomeDone       Outcome = "done"
	OutcomeCannotRun  Outcome = "could not be run at all"
	OutcomeIncomplete Outcome = "ran and did not finish"
	OutcomeLimit      Outcome = "a limit you set stopped it"
)

type ProgramEndedError

type ProgramEndedError struct {
	// Status is the terminal record's word: fail, budget, crashed, or one
	// this build does not know.
	Status string
	// Reason is the one sentence: `senior-dev did not finish: …`.
	Reason string
	// Result is the program's account: its message, what its model claimed
	// and what it observed ([delegateResult]).
	Result string
	// Limit names a refusal made before actual spend reached the ceiling.
	Limit Limit
}

ProgramEndedError is a program's own ending when it did not finish: the status word its terminal record carried, the sentence the task keeps, and its account in full. The run carries it to the session whole (Summary.Program), which draws the row from the fact rather than from the generic "ran and did not finish" — the row that said only that, over an hour of work that had submitted a change and said exactly why it would not stand, told a person nothing they could act on.

func (*ProgramEndedError) Error

func (e *ProgramEndedError) Error() string

type Report

type Report struct {
	Result    string
	Steps     int
	USD       float64
	TokensIn  int
	TokensOut int
	Waiting   bool
	// Verdict is a program's own word for the finished work it handed in —
	// senior-dev's `pass` or `pass-unverified` — when a delegated run's program
	// finished; empty for every other worker ([delegate.Terminal.Verdict]).
	Verdict string
}

Report is what a worker hands back when its task ends well. Result is the task's own account of itself and lands in the store verbatim; Steps, USD and token counts feed the run's receipt, and USD in particular feeds the shared cost counter the Limits govern.

WAITING IS NOT A RESULT. A worker that called `plandb wait` has not finished its task: it parked it, the store released its claim, and it is owed a wake when a dependency or a child moves. Such a worker comes home with Waiting set and no Result, and the supervisor leaves the task open rather than writing a completion.

type Seats

type Seats struct {
	Work  string
	Plan  string
	Check string
	// One is the conversation's model under `--one-model`, and when it is set
	// it is EVERY seat: the three above, the probe no door names, and any role
	// this build has not learned. Nothing here asks the profile or the check
	// seat's environment rung while it is set, because the flag promises that
	// every text call rides the model the person is talking to — and an empty
	// seat falling to the crew row is exactly how a run under it billed models
	// nobody named.
	One string
}

Seats are the run's two resolved seats, as the door that opened the run named them: the work seat every leaf rides and the plan seat every planning task rides. They are the door's own answer — the flag, the environment and the crew it climbed (config.ResolveSeats) — carried so a task launched after the door sits in the seat the person named rather than one the profile happens to hold.

AN EMPTY SEAT IS THE DOOR HAVING NAMED NOTHING, and it falls the way an empty tier always fell: the profile's row for the task's tier, and the worker row beneath that.

Only the work and plan seats climb the ladder on their own; the check seat is the door's own three way answer (config.CheckSeat), carried here as Check: the check flag the person typed, else the plan flag they typed, else empty. A check rides the careful work tier (SeatFor), and an EMPTY Check is the door having named no check model, which falls the way an empty tier always fell: the profile's careful row, the crew's checker. The small row a probe rides has no flag on any door and is the profile's, read below.

type Spec

type Spec struct {
	// Store is the run's plan, opened by the caller and shared with the run's
	// other writers. The root task it was seeded with is the run itself.
	Store *plandb.Store
	// Workspace is the run's own working copy, carried for the worker seat
	// and the landing that follow this loop.
	Workspace string
	// Title and Brief are the run's own words. The store writes a root task's
	// title nowhere but its own open, so the title is the caller's to seed
	// there; the brief is Start's to put down — on a root opened without a
	// description it becomes the root task's description, which is the
	// assignment the root worker reads.
	Title string
	Brief string
	// Slots bounds how many workers run at once, and 0 is no bound; Limits
	// bound the run's cost and its per-task steps. Both pass through to the
	// supervisor as given.
	Slots  int
	Limits Limits
	// Factory makes the worker for every task the run dispatches. Start holds
	// no seat of its own: the root's worker comes from here like the rest.
	Factory WorkerFactory
	// Gate asks the machine before every worker, including the root and wakes.
	Gate AdmissionGate
	// OnHold announces the ids refused on a pass when their set changes.
	OnHold func([]string)
	// OnSpend observes the reconciled cumulative run spend whenever it rises.
	OnSpend func(float64)
}

Spec is what a caller hands Start: the plan store the run lives in, the working copy its workers share, the run's own words, and the factory that resolves every task — the root's included — into the seat that runs it.

type Step

type Step struct {
	Kind string `json:"kind"`
	// Step is the step's number, counted from one over the task across its wakes.
	Step int `json:"step"`
	// Command is what the worker asked the belt to run, as the model spelled
	// it — the command a resumed worker must not repeat blind, and the one
	// address the record has for what this step was.
	Command string `json:"command"`
	// Observation is the head of what came back, cut the way the belt cuts.
	Observation string `json:"observation,omitempty"`
	// FullOutput names the file the whole output was filed in, set only when
	// the step's output was cut — the belt files it beside the worker's
	// transcript, and this is where the next reader finds it.
	FullOutput string `json:"full_output,omitempty"`
	// Writes are the plandb verbs the command ran: the store writes the step
	// made, read off the command's own words, because the verbs are the words
	// the store was addressed by.
	Writes []string `json:"writes,omitempty"`
	// Children are the ids of the tasks this step created under the task, read
	// off the store after the command rather than parsed out of its output.
	Children []string `json:"children,omitempty"`

	// NotRun is established by the engine when the ended tool event is an
	// answer the harness wrote rather than a call the belt ran. The requested
	// command and the answer remain in the record; a surface reads this fact and
	// never the answer's words. False also preserves records written before the
	// field existed.
	NotRun bool `json:"not_run,omitempty"`
	// Refused narrows NotRun to the one kind a person wants to see: AN ACTION THE
	// WORKER ATTEMPTED AND A DOOR REFUSED ([session.Event.Refused]). A NotRun
	// step without it is a correction about the form of the worker's reply, where
	// nothing was attempted on the world. The engine records the difference here
	// because the event is where an attempted action is known; a surface cannot
	// recover it from the record afterwards.
	Refused bool `json:"refused,omitempty"`

	// ExitCode is the command's own exit status when the belt ran one and the
	// recorder knew it: zero for a step that ended, the non-zero code for one
	// that failed. It is a pointer so a step written before this field existed,
	// or one whose exit is unknown, decodes to nil and is never read as a zero a
	// real exit could equal. A holds verdict rests only on a recorded zero exit.
	ExitCode *int `json:"exit_code,omitempty"`

	// The ending line's fields. Steps is the run's whole step count, Result is
	// the worker's own account of the work, and Reason is why the loop ended —
	// a turn that ended, a step cap, a wall.
	Steps  int    `json:"steps,omitempty"`
	Result string `json:"result,omitempty"`
	Reason string `json:"reason,omitempty"`

	// StartedAt and EndedAt are a PROGRAM's own clock on the ending line of the
	// task it was handed: the instant codeaf started its process and the
	// instant that process was gone — the pair the program record carries
	// (delegate.ProgramRecord). They are zero on every other line, on an ending
	// written by a road that never started a process, and on every line a
	// worker of this conversation's own wrote. Step lines never carry them, so
	// the session's mirror of the step line (PlanStep) has no use for them.
	StartedAt time.Time `json:"started_at,omitzero"`
	EndedAt   time.Time `json:"ended_at,omitzero"`

	// ExitsRecorded is stamped true by a build that records each command's
	// exit, on the OPENING line it writes before any step and on the ending
	// line; bashworker.go sets it at both. A reader uses it to tell a record
	// that observed no run, or was cut off before its ending, from one written
	// before exits were recorded: the first refuses a holds verdict that never
	// ran its checks, the second falls back to reading.
	ExitsRecorded bool `json:"exits_recorded,omitempty"`

	// Notes is the notes line's one field: the ids of the task's notes a worker
	// of it has had, handed over or written itself ([trajectoryNotesKind]).
	Notes []string `json:"notes,omitempty"`
}

Step is one line of a task's trajectory. The step lines carry the command, the observation head, the path of the whole output when the belt filed one, the plandb verbs the command ran, and the ids of the children it created; the ending line carries what the worker said last, how many steps it took and why its loop ended. The two shapes share the type and never share a line: Kind says which one a line is.

func Trajectory

func Trajectory(storeDir, id string) ([]Step, error)

Trajectory reads one task's recorded steps back, in the order they were appended. A task that has never run has no file and answers with no steps and no error — the resume road reads it as "no predecessor left anything behind". A line that will not parse is skipped, not reported: a run interrupted mid-append left a half-written last line, and the record is about effects, not about the byte the process died on.

type Summary

type Summary struct {
	Outcome Outcome
	// Result is the root's own result: what the run's last worker reported
	// when the tree finished whole, and empty whenever it did not.
	Result string
	// Failure is the run's own account when it did not finish: the root
	// worker's error, kept so the receipt can say why.
	Failure string
	// Limit is which bound a person set ended the run, and empty on every
	// run that did not end on one. The outcome word is the same sentence for
	// both limits; this is what tells them apart.
	Limit Limit
	// Program is how a delegated run's program ended when it ended without
	// finishing: its status word and its own account ([ProgramEndedError]).
	// Nil for a run that finished, and for every run no program worked.
	Program *ProgramEndedError
	// Verdict is a delegated run's program's own word for the work it
	// finished ([Report.Verdict]): senior-dev's `pass` or `pass-unverified`.
	// Empty for every other run.
	Verdict string
	// Cut is every task the run's own ending cut mid-flight, by store id: its
	// wall, its spend ceiling, or a person's stop ended the context their
	// workers ran under. A task that failed on its own before the ending is
	// not here. This is the fact a surface draws those rows with, so a row the
	// person's bound took down is never read as a fault; it is carried typed
	// and never parsed out of a stored error sentence.
	Cut       []string
	Nodes     int
	Steps     int
	USD       float64
	TokensIn  int
	TokensOut int
	Seconds   float64
}

Summary is what a run came to, in the figures a headless caller prints beside its exit code: the outcome word off the same ladder the envelope speaks, the root's result where a deliverable goes, the run's size — every worker launched, every step its workers reported — what they cost and used, and the wall the run took.

type Supervisor

type Supervisor struct {
	// Owner is the process that holds this run's claims: "<hostname>:<pid>",
	// so each claim names the process answerable for it and a claim nobody
	// touches reads stale. It is set from the running process when the
	// supervisor is built; a test sets it to stand in for a second process
	// sharing one store.
	Owner string
	// contains filtered or unexported fields
}

Supervisor is the launch loop over one plan store. It claims ready leaves as the task's own agent — the name its finish command answers to — and records the process that holds the claim beside it, so a run is owned per process: a claim a dead process left behind is released and taken over by the next one. It starts a worker for each claim under a slot bound and writes every worker's ending back into the store. The root task is the supervisor's own: no worker completes it, and it is completed once every other task in the store is terminal.

func NewSupervisor

func NewSupervisor(store *plandb.Store, workspace string, slots int, limits Limits, factory WorkerFactory) *Supervisor

NewSupervisor builds a run over store. The workspace is the run's own working copy, carried for the worker seat and the landing that follow this loop; slots bounds how many workers run at once; limits bound the run's cost and, per task, its steps.

A SLOT COUNT BELOW ONE IS NO BOUND AT ALL. That is the word the setting the chat door reads gives it — `task.parallel` is 0 out of the box and 0 means no limit (internal/config's DefaultTaskParallel) — and the door passes the figure through as given. This constructor used to read the same 0 as 1 "to keep a misconfigured run alive", which quietly ran every task a conversation put on the harness one worker at a time while the setting beside it promised no limit. Machine pressure is checked by the admission gate at each launch; the number of workers is not itself the resource.

func (*Supervisor) Run

func (s *Supervisor) Run(ctx context.Context) Outcome

Run drives the run to one of the outcome words and returns it. Each pass reads the ready set, claims every ready leaf whose ancestors are not cancelled, and starts one worker per claim, bounded by slots. A pass runs when a worker returns and on the idle timer, so a task added through the store while the loop waits is launched without waiting for anything else.

NO WORKER OUTLIVES ITS RUN. The word this answers is the caller's licence to close the store, land the working copy or drop the process, so every road out of the loop ends the workers still in flight through their contexts and waits for each of them before answering — [Supervisor.drain], on all four roads. The road that needs it most is the tree's completion: the run's own row is written the moment its last descendant lands, and a coordinator still in its turn is a worker the completed tree left behind, with a spend row and a trajectory ending still to write.

The run is over when the root task is terminal, when the cost limit has been crossed and every worker already launched has come home, or when the context ends. A run that ends on anything but the root's own completion leaves the store as it stands — an open root is a run a later pass can pick up — which is why the limit and the context end no store writes of their own.

type Worker

type Worker interface {
	Run(ctx context.Context, task plandb.Task) (Report, error)
}

Worker is one task's executor. The supervisor never talks to a model itself: it launches a Worker per claimed task, hands it the task as the store recorded it, and writes the answer back under the name the task was claimed with. A Worker that cannot finish returns an error and the task fails; a Worker that finishes returns a Report and its result is written.

type WorkerFactory

type WorkerFactory func(task plandb.Task) Worker

WorkerFactory is how the supervisor makes a Worker. The factory sees the task before the worker does, so a real factory resolves whatever the task carries — its role, its kind — into the seat that runs it. It is called once per launch, on the supervisor's own goroutine, and may return nil to say no seat exists for this task; the task then fails rather than hangs.

func CrewFactory

func CrewFactory(store *plandb.Store, workspace, profileDir string, seats Seats, standing string, completerFor func(model string) session.Completer, sourceSets ...modelsource.Set) WorkerFactory

CrewFactory is the run's WorkerFactory: it seats each task in the model its role's seat names — the door's resolved seat where the door named one, the profile's tier row otherwise — and builds the bash-belt worker the run hosts it in already on that model.

THE ROLE IS READ FROM THE STORE, NOT FROM THE TASK HANDED OVER. SeatFor is handed plandb.Store.RoleOf's answer, so a leaf that split mid-work seats as a planner for its coordinating turns without a word on the task being rewritten, and the seat follows the shape as it stands at this launch.

THE PROFILE ANSWERS THE MODEL, through config.TierSeatAt, the same read a conversation and the settings sheet make; the seat it answers carries the rung it came from, but only the model travels here, because a worker is built at a path that has no surface to print the rung on. completerFor builds the seat's provider from the resolved model — a run hands the door's own, and a test records which model it was asked for.

A TIER WITH NO MODEL FALLS TO THE WORKER ROW, and a worker row that is empty too is a task no model can run: the factory answers a worker that refuses rather than one built on an empty model, and its error names the tier, because the row a person has to go and fill is the one the message says. Empty here is not the same as never held: config.TierSeatAt answers this build's default for a tier key the profile has never held and only a row CLEARED on purpose reads empty, so a fallback means somebody emptied a row rather than that the profile is old.

func DelegateFactory

func DelegateFactory(store *plandb.Store, workspace string, program delegate.Delegate, setup DelegateSetup, limits Limits, rest WorkerFactory) WorkerFactory

DelegateFactory is the run's WorkerFactory for a delegated run: the root task is the program's, and every other task the run seats — the review round's check, and nothing else, because a delegated run is a run of one task — falls to the factory it wraps, which is the crew's.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL