Documentation
¶
Overview ¶
Package executor runs a session's lead queue: dispatch, retry, replan, settle.
One lead at a time per worker, each with its own budget reservation taken before the work and settled after it, so a lead that overruns is visible as a flagged overshoot rather than a silent overspend (§8.2).
The loop ends on a drained queue, a ceiling, a wall clock, or a fatal error. A fatal error cancels the batch in flight and stops the loop leasing more — so the blast radius is one batch rather than one lead, which is what --workers 1 buys back.
Index ¶
Constants ¶
const DefaultWorkers = planner.DefaultReplanEvery
DefaultWorkers is the per-session pool size when Workers is unset.
Equal to planner.DefaultReplanEvery, and that is the whole reason for the number. batchSize never runs past a replan boundary, so a pool larger than the replan cadence has workers that can never all be busy: with the previous default of 4 against a cadence of 3, the fourth worker was unreachable in every shipped configuration while three comments, the daemon's startup banner and the README all advertised four — and "four sessions of four workers is sixteen concurrent leads" was wrong by a third.
Tying the two together means the advertised number is the real one. Raising concurrency now means raising both, deliberately, and paying the planner cost §9.1 exists to control.
const MaxAttempts = 3
MaxAttempts caps retries of a whole lead (§9.5).
Deliberately small, and smaller than it looks. A retry re-runs the entire lead — search, fetches, every model call — and all of it is charged again. The LLM layer already retries individual calls with backoff, so reaching here means something outside a single model call failed, and three full re-runs of a lead is already an expensive way to find that out.
const MaxWorkers = 16
MaxWorkers clamps Workers however it was configured.
The ceiling is about what leaves this machine, not about goroutines. Each worker holds a lead, and a lead is searches and fetches against real hosts; a misread config or a fat-fingered flag should not be able to point an unbounded fan of requests at one domain from a user's address. The per-domain rate limiter still applies underneath.
Variables ¶
This section is empty.
Functions ¶
func Backoff ¶
Backoff is the wait before attempt n (1-based), with jitter.
Jitter matters more than the curve here. Without it, several leads that hit the same rate limit retry in lockstep and hit it again together, which is how a brief throttle becomes a sustained one.
func DeadEndCause ¶
DeadEndCause maps an error to the short label the digest groups by (§9.1).
Grouped, so the planner sees "bot_block ×20" rather than twenty lines. The label has to be an enum-ish constant for that to work — an error string with a URL in it groups into a population of one.
func EffectiveWorkers ¶
EffectiveWorkers is how many leads a given Workers setting actually permits, before the per-batch bounds in batchSize narrow it further.
Exported because a caller has to be able to ask. cmd/mole's cassette guard checked the raw flag, so --workers 0 — which means "use the default" — passed a check meant to enforce serial execution and then ran a pool.
Types ¶
type Event ¶
type Event struct {
Phase string // "planning", "executing", "replanning", "lead", "cached"
// Detail is a short human-readable note, already formatted.
Detail string
}
Event is a step the caller may want to show.
type Executor ¶
type Executor struct {
Store store.Store
Ledger *budget.Ledger
Queue *queue.Queue
Planner *planner.Planner
// Verifier builds the claim graph (§11). Nil skips verification entirely,
// which is a supported configuration: the research still runs, claims still
// carry verified quotes, and derived confidence stays 0 rather than being
// invented.
Verifier *verifier.Verifier
Actors map[core.ActorType]actors.Actor
Log *slog.Logger
// Owner identifies the PROCESS holding a lease — "cli" or "daemon" — so the
// two never claim each other's leads (§9.4).
//
// Not per worker, despite what this said before the pool existed. Workers in
// a session share it, and nothing wants otherwise: a lease is claimed for a
// specific lead in a transaction, so the lead is already the unit of
// identity, and the heartbeat renews that lease rather than anything owned
// by a worker. Boot recovery sweeps by age across all owners.
Owner string
// Workers is how many leads this session runs at once. Zero takes
// DefaultWorkers.
//
// Per session rather than per process: the supervisor already bounds how many
// sessions run at once, and one shared pool would let a single large session
// starve every other one. The product of the two is what a person actually
// has to reason about — at the defaults, four sessions of three workers is
// twelve concurrent leads and twelve outbound fetches — so both numbers stay
// visible instead of being folded into one.
Workers int
// Progress reports phase transitions as they happen. Optional.
//
// Not decoration. Planning is a single model call with no output until it
// returns, and on a local model that is minutes of silence — three runs were
// killed by hand because the CLI printed a header and then nothing, which
// reads as a hang rather than as work.
Progress func(Event)
// Cache holds artifacts already researched in this session (§9.3). Shared
// with the actor, so a lead-level hit and a URL-level hit are the same
// cache and one lead's fetches serve another's.
Cache *cache.Cache
// Pricing and CheapModel convert a USD reservation into the token ceiling
// an actor can act on. CheapModel names the tier chunk mining actually
// uses; pricing the ceiling off the strong model would set it several
// times too low. Both empty leaves the actor's configured budget in place.
Pricing *pricing.Table
CheapModel string
// Estimator sizes reservations. Nil uses a fresh one for the session's
// budget unit.
Estimator *budget.Estimator
// Now and Jitter are injectable for tests.
Now func() time.Time
Jitter func() float64
// Sleep waits between retries. Injectable so a test does not spend the
// backoff in real time.
Sleep func(context.Context, time.Duration) error
}
Executor runs one session to completion.
type Result ¶
type Result struct {
SessionID string
Status core.SessionStatus
Digest *planner.Digest
Claims []core.Claim
LeadsRun int
LeadsFailed int
LeadsCached int
Replans int
// CacheStats reports whether the cache earned its keep. An unmeasured
// cache is an assumption.
CacheStats cache.Stats
// Verification totals across every pass (§11).
VerifyPasses int
ClaimsVerified int
EdgesWritten int
Contradictions int
FollowUpsQueued int
// VerifyDegraded is the last reason a pass could not finish, if any.
VerifyDegraded string
// Spent is the settled total in the session's budget unit.
Spent int64
// StoppedBecause names the ceiling or condition that ended the run, for
// the report and the trace.
StoppedBecause string
}
Result is what a session produced.