schedule

package
v0.55.0 Latest Latest
Warning

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

Go to latest
Published: Sep 9, 2026 License: Apache-2.0 Imports: 2 Imported by: 0

Documentation

Overview

Package schedule decides when the agent scans.

Everything here is derived from the machine's own identity rather than from randomness. That gives two properties at once: a fleet is spread out instead of arriving together, and the answer is the same after a restart, so a machine does not drift to a new time every time the service bounces.

Index

Constants

View Source
const (
	SolvePollBusyInterval = 10 * time.Second
	SolveBusyWindow       = 5 * time.Minute
)

SolvePollBusyInterval is how often the agent asks while somebody is working with it, and SolveBusyWindow is how long that lasts after the last piece of work.

One approved change is two round trips: the machine collects the job and sends back a preview, then collects the approval and runs it. At one question a minute that is a two minute wait for a change that takes a second, and the person spends it watching a spinner. Reported from a real session.

The window is what keeps it quick while the person reads the preview and decides, which takes longer than one pass. It ends on its own, so a fleet of machines nobody is using goes back to one question a minute instead of six.

View Source
const HeartbeatInterval = 15 * time.Minute

HeartbeatInterval is how often the agent says "I am still here".

It is what lets the dashboard tell a switched-off server apart from a broken agent. At sixty servers it is about 5,800 tiny requests a day.

View Source
const SolvePollInterval = time.Minute

SolvePollInterval is how often the agent asks whether there is work for it.

This is what decides how long a person waits after clicking approve, so it is the shortest rhythm in the loop. A minute per machine is about 1,440 questions a day each — more than the heartbeat's 96, and still a tiny request that reads one row. Much faster than this would buy a feeling of speed no ops fix needs; much slower and an approved fix would sit there while somebody watched.

Nothing listens on the customer's server for this. The agent asks us, always.

Variables

This section is empty.

Functions

func DailyOffset

func DailyOffset(machineID string) time.Duration

DailyOffset is how far after midnight UTC this machine scans.

Without it, sixty servers all scan at 03:00 and arrive at our API and our LLM budget in one spike every night.

func Next

func Next(machineID string, lastScan, startedAt, now time.Time) time.Time

Next reports when this machine should scan.

A machine that has been switched off for a week gets one catch-up scan, not a week of missed ones.

startedAt is when this agent process began, and the catch-up is anchored to it rather than to now. That matters: anchoring to now would push the target forward every time the loop asked, and an overdue scan would never arrive.

Types

type Action

type Action struct {
	Scan      bool
	Heartbeat bool

	// Wait is how long to sleep when there is nothing to do. It is capped at
	// the Solve poll interval so the loop stays responsive: a daemon that sleeps
	// for twenty hours cannot be told anything, and Solve needs it awake to
	// collect work somebody has just approved.
	Wait time.Duration
}

Action is what the service loop should do on this pass.

Keeping the decision in a pure function is deliberate: the loop around it is then trivial, and every rule about when we touch a customer's server can be tested without touching one.

func Decide

func Decide(machineID string, lastScan, lastHeartbeat, startedAt, now time.Time) Action

Decide reports what the loop should do now.

Jump to

Keyboard shortcuts

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