Documentation
¶
Overview ¶
Package implement is the run machinery an autonomous implementation run calls: the run's shared state in the machine-scoped store, the sessions that join it, the claim that makes a record the unit of exclusion between them, the bounds that keep a second session from becoming a hazard, and the run log the comparison of division modes is derived from (itd-2609221656373558, spc-2609221657588816).
Everything lives under one directory per repository:
~/.abcd.noindex/runs/<root-sha>/<YYYY-MM-DD>.jsonl the run log, one event per line ~/.abcd.noindex/runs/<root-sha>/claims/<record>.json one claim per record ~/.abcd.noindex/runs/<root-sha>/sessions/<id>.json one record per joined session
The key is the repository's root-commit SHA, the same key and the same full form the transcript, history and voyage stores use (internal/core/history's location.go says why: a checkout moves, is renamed and is cloned twice, while its root commit changes under none of that). Two sessions in two worktrees of one repository therefore share one directory and no repository file.
The package is the seed the implement loop builds on (itd-2609201916151817: `implement step` and `implement receipt`) and the pacing verb reads (itd-2609201925079472): a Run is the handle a later state file hangs off, and the log is the measurement both inherit. Core never writes to stdout; the CLI front door formats what these functions return.
Index ¶
- Constants
- Variables
- func IsNetworkFailure(stderr string) bool
- func LoadSites() []string
- func LoggableEvents() []string
- func OutageKinds() []string
- func OutageServices() []string
- func ParseLog(name string, data []byte) ([]Event, []Unparsed)
- func ReadingCorpus(repoRoot string) ([]string, error)
- func RemoteProbe(repoRoot string) func() (bool, string)
- func RequiredFields(event string) []string
- func TouchesReadingCorpus(corpus, paths []string) string
- type Claim
- type ClaimRequest
- type ClaimResult
- type ClaimState
- type CoverageGap
- type Event
- type Evidence
- type FieldGap
- type HeldError
- type JoinResult
- type LeaveResult
- type LoadAverages
- type LoadLimits
- type LoadOtherStrays
- type LoadOwnStray
- type LoadRemedy
- type LoadRequest
- type LoadResult
- type LoadRunLog
- type Mode
- type ModeTally
- type Outage
- type OutageEnd
- type OutageProbe
- type OutageReport
- type OutageSpan
- type OutageWaitError
- type Outages
- func (o *Outages) Ack(session string) (Outage, error)
- func (o *Outages) Clear(session, reason string) (OutageEnd, error)
- func (o *Outages) Current() (*Outage, error)
- func (o *Outages) ProbeIfDue(session string, p Prober) (ProbeOutcome, error)
- func (o *Outages) Record(session, service, kind, lane, what string) (Outage, error)
- type ProbeLeaseHolder
- type ProbeOutcome
- type Prober
- type Report
- type Role
- type Run
- func (r *Run) AgentsAlive(s Session) (int, error)
- func (r *Run) Check(session string, stage Stage, paths []string) (Verdict, error)
- func (r *Run) Claim(req ClaimRequest) (ClaimResult, error)
- func (r *Run) Claims() ([]ClaimState, error)
- func (r *Run) Compare() (Report, error)
- func (r *Run) CurrentMode() (WindowState, bool, error)
- func (r *Run) Join(id string, role Role, model, reason string, ceiling int) (JoinResult, error)
- func (r *Run) Joined(id string) (Session, error)
- func (r *Run) Leave(id, reason string) (LeaveResult, error)
- func (r *Run) Log(session, event string, fields map[string]string) (Event, error)
- func (r *Run) Outage() *Outages
- func (r *Run) ReadLog() ([]Event, []Unparsed, error)
- func (r *Run) Release(session, record string) (Claim, error)
- func (r *Run) Sessions() ([]Session, error)
- func (r *Run) SetMode(session string, m Mode, window int) (WindowState, error)
- type Schedule
- type ServiceResult
- type Session
- type SessionContext
- type SessionTally
- type Stage
- type Unparsed
- type UnreadableClaimError
- type Verdict
- type WindowState
Constants ¶
const ( MinLease = time.Minute MaxLease = 24 * time.Hour )
Lease bounds. A lease shorter than a minute lapses under its own gate run; a lease longer than a day outlives any window the run keeps.
const ( SitePreflight = "preflight" SiteEvalHarness = "eval-harness" )
The sites the check runs at.
const ( // LoadOK: nothing to warn about. LoadOK = "ok" // LoadWarning: at least one trigger fired. LoadWarning = "warning" // LoadSkipped: a CI runner, where the check does not run. LoadSkipped = "skipped" // LoadUnchecked: the machine could not be read (an unsupported platform, or // a read that failed) and nothing fired on what could be read. LoadUnchecked = "unchecked" )
The four statuses.
const ( LimitsDefault = "default" LimitsFile = "file" LimitsDefaultAfterMalformed = "default-after-malformed" )
Where the limits came from.
const ( CoresOnline = "online" CoresRuntime = "runtime" )
The core-count sources.
const ( PrivateNameMask = "[private name]" WithheldNameMask = "[name withheld: private-names layer unreadable]" )
PrivateNameMask replaces an own stray's name the private banned-names layer matches; WithheldNameMask replaces every name when that layer cannot be read.
const ( EventSessionOpen = "session_open" EventSessionClose = "session_close" EventWindowMode = "window_mode" EventClaim = "claim" EventClaimDenied = "claim_denied" EventClaimLapsed = "claim_lapsed" EventClaimReleased = "claim_released" EventBackoff = "backoff" EventLaneOpen = "lane_open" EventLaneClose = "lane_close" EventAgentStart = "agent_start" EventAgentEnd = "agent_end" EventCeilingWait = "ceiling_wait" EventGateRun = "gate_run" EventReview = "review" EventFallback = "fallback" EventStop = "stop" EventRefusal = "refusal" EventPR = "pr" EventCapture = "capture" // EventContext is an orchestrator's context measurement: used_pct (the share // of its context window in use), role and note. The run measures it because // it is also an experiment in keeping a session alive for days. EventContext = "context" // EventCeilingOverrun is a session going over its agent ceiling: alive (the // agents alive), ceiling, lane and minutes (how long it was over). The verb // refuses an agent_start past the ceiling, so an overrun is what the host // did anyway — a fork, an agent started outside the log — and says so. EventCeilingOverrun = "ceiling_overrun" // The evidence events: what an autonomous run records so a later run can be // built to need no person. An intervention is a person acting on the run // (kind, by, what, why, autonomy_gap — what abcd or the host would need so // no person is needed — and optionally at and detected_after_min); a stop is // a stall (cause, and optionally last_productive, noticed_after_min and // recovery); a decision is a judgement call a person would normally make // (what, alternative, why, and optionally at). EventIntervention = "intervention" EventDecision = "decision" // EventLoad is the load check's warning (itd-2609231434459890), written by // `implement load` alone and only when it warns inside a live run. The run's // hand-kept load samples share the name and carry no `triggers`, which is how // a reader tells the two apart. EventLoad = "load" )
Event names. The first group is written only by this package's verbs, so the log cannot claim a join, a window or a claim that the run state does not hold.
const ( ServiceNetwork = "network" ServiceModel = "model" )
The services an outage can take down.
const ( // OutageHost is the host's own model calls stalling. OutageHost = "host" // OutageAgent is a sub-agent coming back failed mid-task. OutageAgent = "agent" // OutageTool is a tool's network call failing: git push, gh, a download. OutageTool = "tool" )
The kinds of report: what noticed the lost connection.
const ( OutageOpen = "open" OutageGaveUp = "gave_up" )
An outage's status. An outage that ends is removed, so the record only ever holds one of these two.
const ( EventOutageStart = "outage_start" EventOutageProbe = "outage_probe" EventOutageEnd = "outage_end" EventOutageGiveUp = "outage_give_up" )
The outage events, written by the outage verbs alone.
const CoverageGapAfter = 6 * time.Hour
CoverageGapAfter is how long before the run's last line a measured event's last line may fall before the report names its coverage as stopping.
const DefaultLease = 2 * time.Hour
DefaultLease is how long a claim holds when the caller names no lease: long enough for a lane's build-and-gate cycle, short enough that a session that died mid-lane frees its record within the same working window.
const JoinGrace = time.Minute
JoinGrace is how long before a window_mode a session_open may be logged and still count in that window.
const MaxCeiling = 64
MaxCeiling bounds a stated agent ceiling.
The ceiling is a session's own limit on the agents it runs at once — for the second session, on top of the first session's (itd-2609221656373558, criterion 5). The session states it on joining, and the record and the session_open line carry it. abcd runs no agent, so what it holds the ceiling against is what the session declares: the agents its own agent_start and agent_end lines leave alive (Run.AgentsAlive). An agent_start past the ceiling is refused and the refusal logged, and every check reports the count beside the ceiling. An agent the session never logs — a fork, one the host started outside the log — is invisible, so the count is a discipline the session keeps with the verb's help, not a census of processes (iss-2609240646542516).
const NetworkProbeTimeout = 20 * time.Second
NetworkProbeTimeout bounds the network probe's call to the remote.
const ProbeLease = 2 * time.Minute
ProbeLease is how long a probe's holder keeps it: longer than the network probe's own timeout, so a live probe never lapses, and short enough that a holder that died mid-probe holds the other lanes for minutes, not hours.
const ProbeResultWait = ProbeLease - NetworkProbeTimeout - 10*time.Second
ProbeResultWait bounds how long a finished probe waits for the run's lock to write its result: well inside the lease the probe holds (ProbeLease less the network probe's own bound), so a result that waited is still the lease holder's to write.
const UnreadableClaimGrace = time.Minute
UnreadableClaimGrace is how long a claim file nobody can parse holds its record, counted from the file's modification time. Every claim is written under the run's lock in one create, so an unparseable file is a writer killed between the create and the write; the grace is only long enough that a reader racing that writer backs off rather than taking the record.
const UnsetMode = "unset"
UnsetMode labels the events logged before any window opened.
Variables ¶
var CoverageEvents = []string{EventLaneOpen, EventLaneClose, EventAgentStart, EventAgentEnd, EventGateRun}
CoverageEvents are the hand-kept events the report's figures rest on and a run writes throughout, so their stopping partway is a gap, not a lull.
var DefaultSchedule = Schedule{ Waits: []time.Duration{time.Minute, 5 * time.Minute, 10 * time.Minute}, Hourly: time.Hour, HourlyFor: 8 * time.Hour, }
DefaultSchedule is the product thinker's (interview of 2026-10-09): one minute, five, ten, then hourly for up to eight hours.
var ErrContention = errors.New("contention")
ErrContention is the class of every "someone else holds it" outcome: a record another session has claimed, or the run state locked by another session's mutation. It is not a fault: the caller backs off and tries other work. The CLI maps it to exit 3.
var ErrRefused = errors.New("refused")
ErrRefused is the class of every refusal: an input the verb does not recognise, a session that has not joined, or a bound the caller's role does not permit. Nothing is written for the refused act (the refusal itself may be logged). The CLI maps it to exit 2.
var InterventionKinds = []string{
"session_open", "account", "ruling", "restart", "close_session", "file_restore", "permission", "other",
}
InterventionKinds is the closed vocabulary of an intervention's kind.
var LimitsFileDisplay = abcdhome.Display(machineload.LimitsFileName)
LimitsFileDisplay is the settings file as every report names it.
Functions ¶
func IsNetworkFailure ¶ added in v0.13.4
IsNetworkFailure reports whether a git or gh command's stderr says the network failed it: a name that did not resolve, a connection refused, timed out, reset or cut off. A refusal by the other end — an authentication failure, a hook's refusal, a rejected push — or a merge conflict is not one, even when its text also quotes a network-sounding line, because waiting does not fix it.
func LoggableEvents ¶
func LoggableEvents() []string
LoggableEvents returns the events `implement log` accepts, for help text and shell completion.
func OutageKinds ¶ added in v0.13.4
func OutageKinds() []string
OutageKinds returns the closed kind vocabulary.
func OutageServices ¶ added in v0.13.4
func OutageServices() []string
OutageServices returns the closed service vocabulary.
func ReadingCorpus ¶
ReadingCorpus is what "a lane that touches the reading corpus" means, derived from the checkout's committed preset file rather than restated here: the union of every position's object.paths, plus the preset file itself, whose recalibration serialises such lanes (iss-2609211105023379). A lane that edits one of those paths moves what a cold reading is handed, and so the measured windows the first session recalibrates. An entry names a file or a directory; a directory covers everything beneath it.
It is read through reading.LoadPresets, the loader the reading verb itself uses, so the two can never disagree about what the corpus is, and it refuses what that loader refuses: an untracked, symlinked or unparseable preset file. A corpus that cannot be derived is an error, never an empty list — the caller fails closed on it.
func RemoteProbe ¶ added in v0.13.4
RemoteProbe is the production network probe: `git ls-remote --exit-code origin HEAD` in repoRoot through the isolated git helper, bounded by NetworkProbeTimeout. The connection is back when the remote answers — with HEAD (exit 0), without it (exit 2), or with a refusal that is not a network failure, since a remote that refuses has been reached. It is down when git prints a network failure or gives no answer in time. Credentials are never asked for at a terminal: the probe's askpass fails at once.
func RequiredFields ¶ added in v0.12.0
RequiredFields returns the fields `implement log` requires on event, each as its accepted names ("minutes|wall_minutes|wall_min"), for help text and the report's missing-field count.
func TouchesReadingCorpus ¶
TouchesReadingCorpus returns the first path that falls in corpus — equal to an entry, or beneath one — or "" when none does.
Types ¶
type Claim ¶
type Claim struct {
Record string `json:"record"`
Session string `json:"session"`
Lane string `json:"lane"`
ClaimedAt time.Time `json:"claimed_at"`
ExpiresAt time.Time `json:"expires_at"`
}
Claim is one claim file's content.
type ClaimRequest ¶
type ClaimRequest struct {
Session string
Record string
Lane string
// Lease is the claim's lifetime; zero means DefaultLease.
Lease time.Duration
// Paths are the repository-relative files the lane expects to touch, when the
// caller knows them. For the second session a path in the reading corpus —
// or any path, when the corpus cannot be derived — refuses the claim (see
// ReadingCorpus).
Paths []string
}
ClaimRequest is what a session asks for.
type ClaimResult ¶
type ClaimResult struct {
Claim Claim `json:"claim"`
// Renewed is true when the session already held the record and the lease was
// extended instead of a second claim being taken.
Renewed bool `json:"renewed"`
// Lapsed is the expired claim this one replaced, when there was one.
Lapsed *Claim `json:"lapsed,omitempty"`
}
ClaimResult is what a granted claim reports.
type ClaimState ¶
type ClaimState struct {
Claim
Live bool `json:"live"`
// Unreadable marks a claim file nobody can parse: it carries only its
// record, and its lease is the grace after the file was written.
Unreadable bool `json:"unreadable,omitempty"`
}
ClaimState is a claim as read at a moment: whether its lease still holds.
type CoverageGap ¶ added in v0.12.0
type CoverageGap struct {
Event string `json:"event"`
Lines int `json:"lines"`
Last time.Time `json:"last"`
// RunLast is the run's last line, load samples aside.
RunLast time.Time `json:"run_last"`
HoursBefore float64 `json:"hours_before"`
}
CoverageGap is a measured event whose lines stop partway through the run.
type Event ¶
type Event struct {
TS time.Time `json:"ts"`
Session string `json:"session"`
Event string `json:"event"`
Fields map[string]json.RawMessage `json:"-"`
}
Event is one line of the run log as it was read: the three fixed fields parsed, and every field (the fixed three included) kept as raw JSON so a reader can take what it understands and leave the rest.
type Evidence ¶ added in v0.12.0
type Evidence struct {
Interventions int `json:"interventions"`
InterventionsByKind map[string]int `json:"interventions_by_kind"`
// DetectedAfterMinutes sums the interventions' detected_after_min: how long
// the needs went unnoticed.
DetectedAfterMinutes float64 `json:"detected_after_minutes"`
Stops int `json:"stops"`
// StopNoticedAfterMinutes sums the stops' noticed_after_min.
StopNoticedAfterMinutes float64 `json:"stop_noticed_after_minutes"`
Decisions int `json:"decisions"`
}
Evidence is the run's evidence events counted over the whole run: what a run that needs no person would have to do without.
type FieldGap ¶ added in v0.12.0
type FieldGap struct {
Event string `json:"event"`
Field string `json:"field"`
Lines int `json:"lines"`
Of int `json:"of"`
}
FieldGap is a field `implement log` requires of an event, and how many of the log's lines of that event lack it.
type HeldError ¶
type HeldError struct {
Holder Claim
}
HeldError is the contention a refused claim returns: the record and the session that holds it.
type JoinResult ¶
type JoinResult struct {
Session Session `json:"session"`
// Rejoined is true when the session's record already existed with the same
// role: a resumed session opens again rather than being refused.
Rejoined bool `json:"rejoined"`
}
JoinResult is what Join reports.
type LeaveResult ¶
LeaveResult is what Leave reports.
type LoadAverages ¶
type LoadAverages struct {
One float64 `json:"one"`
Five float64 `json:"five"`
Fifteen float64 `json:"fifteen"`
}
LoadAverages are the three load averages.
type LoadLimits ¶
type LoadLimits struct {
StrayMinutes int `json:"stray_minutes"`
ExtremeLoad float64 `json:"extreme_load"`
Source string `json:"source"`
StrayFromFile bool `json:"stray_from_file"`
ExtremeFromFile bool `json:"extreme_from_file"`
// Malformed says why the settings file is unusable, when it is: a line
// number and a fault class, never a value.
Malformed string `json:"malformed,omitempty"`
}
LoadLimits are the limits in force and where they came from.
type LoadOtherStrays ¶
LoadOtherStrays is every other account's strays: a count and a CPU total in cores, rounded to one decimal, and nothing else.
type LoadOwnStray ¶
type LoadOwnStray struct {
Name string `json:"name"`
PID int `json:"pid"`
PGID int `json:"pgid"`
AgeS int64 `json:"age_s"`
CPUPct int `json:"cpu_pct"`
}
LoadOwnStray is one of the caller's own strays as it is printed and logged.
type LoadRemedy ¶
type LoadRemedy struct {
// Form is "group" (pgrep -g, then kill -- -PGID) or "process" (ps -p, then
// kill PID).
Form string `json:"form"`
PGID int `json:"pgid,omitempty"`
PIDs []int `json:"pids,omitempty"`
PID int `json:"pid,omitempty"`
// Why says why a process form is not the group form.
Why string `json:"why,omitempty"`
}
LoadRemedy is one printed way to stop some of the caller's own strays.
type LoadRequest ¶
type LoadRequest struct {
// Site is where the check runs: SitePreflight or SiteEvalHarness.
Site string
// Getenv reads the environment; nil is os.Getenv.
Getenv func(string) string
// Read reads the machine; nil is machineload.Read.
Read func() (machineload.Snapshot, error)
// Home is the caller's home, where ~/.abcd.noindex/load-limits lives; "" resolves it.
Home string
// RepoRoot is the checkout the check runs in, whose private banned-names
// layer scrubs the own strays' names; "" is outside any checkout.
RepoRoot string
// RootSHA keys the run state; "" derives it from RepoRoot.
RootSHA string
// Self is the invocation; the zero value is this process.
Self machineload.Self
// Now is the clock for the run-log line; nil is time.Now.
Now func() time.Time
// GOOS names the platform in the cannot-check reason; "" is runtime.GOOS.
GOOS string
}
LoadRequest is one check. Every field but Site has a working zero value; the others exist so a test never depends on the real machine, environment or home.
type LoadResult ¶
type LoadResult struct {
Status string `json:"status"`
Site string `json:"site"`
Load *LoadAverages `json:"load"`
Cores int `json:"cores"`
CoresSource string `json:"cores_source,omitempty"`
Limits *LoadLimits `json:"limits"`
Triggers []string `json:"triggers"`
OwnStrays []LoadOwnStray `json:"own_strays"`
OwnStraysMore int `json:"own_strays_more"`
OtherStrays LoadOtherStrays `json:"other_strays"`
Remedy []LoadRemedy `json:"remedy"`
NamesWithheld bool `json:"names_withheld,omitempty"`
WithinPreflight bool `json:"within_preflight"`
RunLog LoadRunLog `json:"run_log"`
// Reason says why the check was skipped or could not read everything.
Reason string `json:"reason,omitempty"`
}
LoadResult is the check's whole report.
func CheckLoad ¶
func CheckLoad(req LoadRequest) LoadResult
CheckLoad runs the load check once. It never returns an error: a machine it cannot read is a status with a reason, and a run log it cannot write is reported on the result beside a warning that stands.
func LoadResultFromEvent ¶
func LoadResultFromEvent(ev Event) (LoadResult, error)
LoadResultFromEvent reads a logged `load` event back into the warning it records, so a reader renders exactly what was printed. The run-log outcome is not part of the event.
No production code reads it yet; its readers are tests in two packages, this one and the CLI surface's (TestLoggedEventRendersToThePrintedWarning, which needs the surface's unexported renderer). It stays here, exported, because Go has no test-only export across packages and the decoding needs the unexported loadEvent, so moving it into a test file would duplicate the event's shape.
type LoadRunLog ¶
type LoadRunLog struct {
Logged bool `json:"logged"`
Session string `json:"session,omitempty"`
Error string `json:"error,omitempty"`
}
LoadRunLog is what happened to the run-log event.
type Mode ¶
type Mode string
Mode is a window's division of work between the sessions. `single` is a window with one session; the three the intent measures are `claim` (a session claims a record before opening its lane), `batch` (the run file assigns whole batches per session) and `split-roles` (the first builds; the second reviews, audits and lands).
const ( ModeSingle Mode = "single" ModeClaim Mode = "claim" ModeBatch Mode = "batch" ModeSplitRoles Mode = "split-roles" )
The four modes.
type ModeTally ¶
type ModeTally struct {
Mode string `json:"mode"`
Windows int `json:"windows"`
WallMinutes float64 `json:"wall_minutes"`
LanesOpened int `json:"lanes_opened"`
LanesLanded int `json:"lanes_landed"`
SecondLanesLanded int `json:"second_session_lanes_landed"`
Collisions int `json:"collisions"`
Lapses int `json:"claims_lapsed"`
Backoffs int `json:"backoffs"`
BackoffMinutes float64 `json:"backoff_minutes"`
AgentMinutes float64 `json:"agent_minutes"`
CeilingWaitMinutes float64 `json:"ceiling_wait_minutes"`
// CeilingOverruns are the ceiling_overrun lines, and CeilingOverrunMinutes
// the minutes over they carry.
CeilingOverruns int `json:"ceiling_overruns"`
CeilingOverrunMinutes float64 `json:"ceiling_overrun_minutes"`
Refusals int `json:"refusals"`
LandedPerHour float64 `json:"lanes_landed_per_hour"`
Sessions []SessionTally `json:"sessions"`
}
ModeTally is one division mode's figures over every window that ran it.
type Outage ¶ added in v0.13.4
type Outage struct {
StartedAt time.Time `json:"started_at"`
// Services are every service the outage has taken down; Down are those not
// yet proven back. The outage ends when Down is empty.
Services []string `json:"services"`
Down []string `json:"down"`
// Kinds are what noticed it, over every report.
Kinds []string `json:"kinds"`
Reports []OutageReport `json:"reports"`
// ReportsDropped counts the reports past maxOutageReports, kept as a count.
ReportsDropped int `json:"reports_dropped,omitempty"`
Probes []OutageProbe `json:"probes"`
NextProbeAt time.Time `json:"next_probe_at"`
// HourlySince is the third failed probe, where the hourly stage begins.
HourlySince *time.Time `json:"hourly_since,omitempty"`
Lease *ProbeLeaseHolder `json:"lease,omitempty"`
Status string `json:"status"`
GaveUpAt *time.Time `json:"gave_up_at,omitempty"`
// NotifyPending is raised once, at the give-up, and lowered by Ack.
NotifyPending bool `json:"notify_pending"`
}
Outage is the run's outage record.
type OutageEnd ¶ added in v0.13.4
type OutageEnd struct {
StartedAt time.Time `json:"started_at"`
EndedAt time.Time `json:"ended_at"`
How string `json:"how"`
Minutes float64 `json:"minutes"`
Services []string `json:"services"`
Kinds []string `json:"kinds"`
Retried []string `json:"retried"`
Probes int `json:"probes"`
}
OutageEnd is how an outage ended.
type OutageProbe ¶ added in v0.13.4
type OutageProbe struct {
At time.Time `json:"at"`
Session string `json:"session"`
OK bool `json:"ok"`
Results []ServiceResult `json:"results"`
}
OutageProbe is one run of the shared probe. OK is true when it left no service down.
type OutageReport ¶ added in v0.13.4
type OutageReport struct {
At time.Time `json:"at"`
Session string `json:"session"`
Service string `json:"service"`
Kind string `json:"kind"`
Lane string `json:"lane"`
What string `json:"what"`
}
OutageReport is one lane's report of a lost connection.
type OutageSpan ¶ added in v0.13.4
type OutageSpan struct {
Start time.Time `json:"start"`
// End is the outage_end or outage_give_up line; nil while it is open.
End *time.Time `json:"end,omitempty"`
// Outcome is "ended" (a probe proved every service back), "cleared" (closed
// by hand), "gave_up", or "open" at the log's last line.
Outcome string `json:"outcome"`
// Minutes is the line's own figure for a closed outage, and for an open one
// the minutes to the log's last line.
Minutes float64 `json:"minutes"`
Services []string `json:"services"`
Kinds []string `json:"kinds"`
Retried []string `json:"retried"`
Probes int `json:"probes"`
}
OutageSpan is one outage as the log records it.
type OutageWaitError ¶ added in v0.13.4
type OutageWaitError struct {
NextProbeAt time.Time `json:"next_probe_at"`
Down []string `json:"down"`
Holder string `json:"holder,omitempty"`
LeaseUntil time.Time `json:"lease_until,omitzero"`
}
OutageWaitError is the contention a caller meets when the probe is not due or another session holds it: it names when the next probe falls due and, for a held probe, the holder and its lease.
func (*OutageWaitError) Error ¶ added in v0.13.4
func (e *OutageWaitError) Error() string
func (*OutageWaitError) Is ¶ added in v0.13.4
func (e *OutageWaitError) Is(target error) bool
Is makes an OutageWaitError an ErrContention.
type Outages ¶ added in v0.13.4
type Outages struct {
// Schedule is the probe schedule; the zero value is DefaultSchedule.
Schedule Schedule
// contains filtered or unexported fields
}
Outages is the run's outage handle.
func (*Outages) Ack ¶ added in v0.13.4
Ack lowers the give-up's notification: the product thinker has been told. It is refused when nothing is pending, so a notification is never raised again by acknowledging it twice.
func (*Outages) Clear ¶ added in v0.13.4
Clear closes the outage by hand, open or given up, with the reason. It logs outage_end (how "cleared") and, because a person or a session stepped in where the shared probe should have, an intervention naming the gap.
func (*Outages) Current ¶ added in v0.13.4
Current returns the outage in force, nil when there is none. It writes nothing and takes no lock: the record is only ever replaced whole.
func (*Outages) ProbeIfDue ¶ added in v0.13.4
func (o *Outages) ProbeIfDue(session string, p Prober) (ProbeOutcome, error)
ProbeIfDue runs the shared probe if it is due and nobody else is running it. Under the run's lock it reads the outage: none is nothing to wait on; one that gave up is refused, because the run has stopped; one whose next probe is not due, or whose probe another live lease holds, is an *OutageWaitError (contention) carrying next_probe_at. Otherwise it takes the lease, releases the lock, probes each service down (prober's function for it; none leaves it unproven), then re-locks and writes the result: every service back ends the outage and logs outage_end; otherwise the failure is counted, the next probe set by the schedule, and a failure at or past the hourly stage's limit gives up, raising notify_pending and logging outage_give_up. A result whose lease lapsed and was taken by another session meanwhile is discarded.
func (*Outages) Record ¶ added in v0.13.4
Record opens an outage, or extends the one open, with a lane's report: the service it lost, what noticed it, the lane and what it was doing. The first report logs outage_start and sets the first probe a schedule's wait away; a later one adds its service to those down (a service proven back and lost again is down again) and its kind to the kinds. A report to an outage that gave up joins it and raises no second notification.
type ProbeLeaseHolder ¶ added in v0.13.4
ProbeLeaseHolder is the session running the probe, and until when it holds it.
type ProbeOutcome ¶ added in v0.13.4
type ProbeOutcome struct {
// Probed is false when there was no outage to probe.
Probed bool `json:"probed"`
Probe *OutageProbe `json:"probe,omitempty"`
// Ended is true when the probe proved every service back; Minutes is then
// how long the outage lasted.
Ended bool `json:"ended"`
Minutes float64 `json:"minutes,omitempty"`
// GaveUp is true when this probe gave up.
GaveUp bool `json:"gave_up"`
// Outage is the record after the probe, nil once it has ended.
Outage *Outage `json:"outage"`
}
ProbeOutcome is what ProbeIfDue did.
type Prober ¶ added in v0.13.4
type Prober struct {
Network func() (ok bool, detail string)
Model func() (ok bool, detail string)
}
Prober is how each service is proven back. A nil function leaves its service unproven, and still down when it was down. Only the services down are probed. Model is the lead's canary verdict: abcd calls no model, and the lead's own turn running is not proof the service is back.
type Report ¶
type Report struct {
Events int `json:"events"`
Unparsed []Unparsed `json:"unparsed"`
Modes []ModeTally `json:"modes"`
// Context is each session's context measurement, by session id.
Context []SessionContext `json:"context"`
// Evidence counts the run's interventions, stops and decisions.
Evidence Evidence `json:"evidence"`
// MissingFields names, per event and required field, the lines lacking it.
MissingFields []FieldGap `json:"missing_fields"`
// Coverage names each measured event whose lines stop partway.
Coverage []CoverageGap `json:"coverage"`
// Leader is the mode with the most lanes landed per wall-clock hour, "" when
// no mode landed a lane or two modes tie. It is a figure, not a verdict: the
// run's report names which mode it would keep, and says why.
Leader string `json:"leader"`
LeaderBasis string `json:"leader_basis"`
// Outages are the run's lost connections, each with how long it lasted and
// what was retried, so a short outage is reported though nobody was told of
// it at the time.
Outages []OutageSpan `json:"outages"`
}
Report is the derived comparison.
type Role ¶
type Role string
Role is a session's standing in the run. The first session is the run: it may cut a release and take any lane, and it never waits on the second. The second is optional and bounded (the intent's decision 3): at most one lane, never the release, never a lane that touches the reading corpus, and it backs off on contention.
The role lives in the session's record in the run state, written when the session joins. It is deliberately not read from the environment: a variable a shell, a task runner or a repository's configuration can set is not a statement the session made, and the bounds key on it. What the record gives is consistency, not authentication — two sessions of one account can each write anything under that account's home — so the bounds are a discipline two cooperating sessions keep, checked at every verb, not a wall against a hostile one.
type Run ¶
type Run struct {
// Dir is the run directory, ~/.abcd.noindex/runs/<root-sha>.
Dir string
// RootSHA is the repository's root-commit SHA the directory is keyed on.
RootSHA string
// RepoRoot is the checkout the run was opened from. The reading corpus the
// second session's bounds check against is read from its preset file.
RepoRoot string
// Now is the clock. Tests set it; nil means time.Now.
Now func() time.Time
// contains filtered or unexported fields
}
Run is one repository's run state. The zero value is not usable; construct it with Open (which creates the directories a mutation needs) or Peek (which creates nothing, for the read-only renders).
func Open ¶
Open returns the run for rootSHA, creating its directories when absent. Every level from ~/.abcd.noindex down is created one at a time and proved a real directory, never through a symlink (fsutil.EnsureRealDirAll), so a planted redirect is refused before anything is written beneath it.
func OpenJoined ¶
OpenJoined returns the run for rootSHA on behalf of a session that must already have joined it: every writer but join. A run directory that does not exist holds no session, so the caller is refused before anything is created — no directory, no lock, no log line. An existing run is opened as Open opens it, and the verb itself refuses a session it does not hold.
func Peek ¶
Peek returns the run for rootSHA without creating anything. A run directory that does not exist reads as an empty run: no sessions, no claims, no log.
func (*Run) AgentsAlive ¶ added in v0.12.0
AgentsAlive counts the agents a session has declared alive: the agents named by its agent_start lines since it joined, each with no agent_end of the same agent after it. It counts what the session wrote and nothing else — abcd runs no agent, and a line the session never wrote (a fork, an agent the host started outside the log) is invisible here — so it is the declared count the ceiling is held against, not a census of processes. Lines naming no agent cannot be matched and are not counted.
func (*Run) Check ¶
Check says whether a session may take a stage, and logs the refusal when it may not. The first session may take every stage. The second is refused:
- the release stage, always — only the first session cuts a release;
- a lane in a split-roles window, where the second only reviews, audits and lands;
- a lane whose paths reach the reading corpus.
Review, audit and land are open to both. A refusal is an ErrRefused-classed error and a `refusal` line in the log; an allowed stage writes nothing.
func (*Run) Claim ¶
func (r *Run) Claim(req ClaimRequest) (ClaimResult, error)
Claim takes req.Record for req.Session. In order, under the run's lock:
- the session must have joined; its role decides the bounds;
- a second session is refused when it already holds a live claim on another record (one lane at a time), when the window's mode is split-roles (the second builds nothing), or when req.Paths reach the reading corpus;
- the claim file is created exclusively. If it exists and this session holds it, the lease is renewed. If another session's lease has passed, the lapse is logged, the stale file removed, and the create retried. If another session's lease holds, the denial is logged naming the holder — and for the second session a backoff is logged beside it — and a *HeldError (ErrContention) is returned.
A granted claim is logged as `claim`. When that line cannot be written the claim is removed again, so the claims directory never holds a claim the log does not.
func (*Run) Claims ¶
func (r *Run) Claims() ([]ClaimState, error)
Claims lists every claim file, live or lapsed, by record.
func (*Run) CurrentMode ¶
func (r *Run) CurrentMode() (WindowState, bool, error)
CurrentMode returns the mode the log last recorded, whoever wrote the line (a hand-written window_mode counts the same as one SetMode wrote). ok is false when no window has been opened; an unrecognised mode word is returned as it was written, and the bounds treat it as no split.
func (*Run) Join ¶
Join records a session in the run and logs its session_open. Nothing signals any other session: joining is a file the joiner writes and a log line, and the first session learns of a second only if it reads the run state. The record is taken by an exclusive create; a session that joins again with the role it holds is a resume, and one that asks for a different role is refused. ceiling is the session's own agent ceiling (see MaxCeiling), zero for none; a resume keeps the ceiling it joined with and refuses a different one.
func (*Run) Joined ¶ added in v0.12.0
Joined returns a joined session's record, or refuses one the run does not hold, writing nothing: for a caller that acts for a session in the run state and must know it has joined before it creates anything of its own.
func (*Run) Leave ¶
func (r *Run) Leave(id, reason string) (LeaveResult, error)
Leave closes a session: it releases every claim the session holds, logs session_close with the reason, and removes the session's record. A session that stops without leaving is covered by its claims' leases instead.
func (*Run) Log ¶
Log appends one event on a joined session's word: the run's hand-kept events (lane_open, agent_end, backoff, …) through the same single-write append the verbs use. The event must be one of LoggableEvents — the verb-owned events are refused, because a hand-written claim the claims directory does not hold is a log that lies — and fields are key=value pairs under the limits above. A value that parses as an integer, a decimal or a boolean is written as a JSON number or boolean when it round-trips to the same text; anything else as a string.
func (*Run) ReadLog ¶
ReadLog reads every day's log in the run directory, in date order. A missing directory is an empty log. Lines that are not a JSON object carrying ts, session and event are returned as Unparsed rather than failing the read: the log is hand-written in part, and a comparison that stopped at the first odd line would report nothing.
func (*Run) Release ¶
Release gives up a session's claim on a record and logs claim_released. Only the holder releases: a session cannot free another's claim, which lapses on its own lease instead.
type Schedule ¶ added in v0.13.4
type Schedule struct {
Waits []time.Duration `json:"waits"`
Hourly time.Duration `json:"hourly"`
HourlyFor time.Duration `json:"hourly_for"`
}
Schedule is when the shared probe runs, as data: the waits after the outage opens and after each of the first failed probes, then a probe every Hourly, for HourlyFor after the hourly stage begins.
func (Schedule) GivesUp ¶ added in v0.13.4
GivesUp reports whether a probe failing at at gives up, for an hourly stage that began at hourlySince: at or after HourlyFor into it.
func (Schedule) Next ¶ added in v0.13.4
Next is when the next probe falls due: the wait for failures failed probes so far, counted from last — the outage's start when nothing has been probed, and otherwise the probe that actually ran, so a late probe moves every one after it rather than bunching them up.
type ServiceResult ¶ added in v0.13.4
type ServiceResult struct {
Service string `json:"service"`
OK bool `json:"ok"`
Detail string `json:"detail,omitempty"`
}
ServiceResult is one service's answer to a probe. A service the probe had no way to prove (no canary verdict for the model) is not OK, and says it is unproven.
type Session ¶
type Session struct {
Session string `json:"session"`
Role Role `json:"role"`
JoinedAt time.Time `json:"joined_at"`
Model string `json:"model,omitempty"`
// Ceiling is the session's own agent ceiling as it stated it on joining,
// zero when it stated none.
Ceiling int `json:"ceiling,omitempty"`
}
Session is a joined session's record.
type SessionContext ¶
type SessionContext struct {
Session string `json:"session"`
Role string `json:"role"`
Events int `json:"events"`
LastUsedPct float64 `json:"last_used_pct"`
LastAt time.Time `json:"last_at"`
}
SessionContext is one session's context measurement across the whole run: how many context lines it logged and the last used_pct among them. It is per session rather than per mode because an orchestrator's context is carried across windows, not reset by them.
type SessionTally ¶
type SessionTally struct {
Session string `json:"session"`
Role string `json:"role"`
LanesOpened int `json:"lanes_opened"`
LanesLanded int `json:"lanes_landed"`
AgentMinutes float64 `json:"agent_minutes"`
BackoffMinutes float64 `json:"backoff_minutes"`
CeilingWaitMinutes float64 `json:"ceiling_wait_minutes"`
CeilingOverruns int `json:"ceiling_overruns"`
Collisions int `json:"collisions"`
}
SessionTally is one session's share of a mode.
type Stage ¶ added in v0.12.0
type Stage string
Stage is a point in a run where the second session's bounds are checked before the session acts, called a stage like the loop's lane stages (rulings BU1 and CM1: a spec's piece is a step, and nothing else is). A lane is opened by a claim (Claim applies the lane bounds itself); Check is for the stages that are not claims, and for a lane whose files are only known once it has been built.
const ( StageLane Stage = "lane" StageRelease Stage = "release" StageReview Stage = "review" StageAudit Stage = "audit" StageLand Stage = "land" )
The stages.
func ParseStage ¶ added in v0.12.0
ParseStage accepts exactly one of Stages.
type Unparsed ¶
type Unparsed struct {
File string `json:"file"`
Line int `json:"line"`
Reason string `json:"reason"`
}
Unparsed is one log line the reader could not use, with why.
type UnreadableClaimError ¶
UnreadableClaimError is a claim file nobody can parse — a session killed between the exclusive create and the write leaves an empty one. It names the file's full path and when it lapses, never a guess at who holds it.
func (*UnreadableClaimError) Error ¶
func (e *UnreadableClaimError) Error() string
type Verdict ¶
type Verdict struct {
Session string `json:"session"`
Role Role `json:"role"`
Stage Stage `json:"stage"`
Mode Mode `json:"mode,omitempty"`
Allowed bool `json:"allowed"`
// Ceiling is the session's own agent ceiling as it joined with it, zero when
// it stated none: reported with every verdict, beside AgentsAlive, so the
// session about to act sees the limit it keeps (see MaxCeiling).
Ceiling int `json:"ceiling,omitempty"`
// AgentsAlive is the count of the session's agents its own log lines declare
// alive (see Run.AgentsAlive): what an agent_start is held against when the
// session stated a ceiling.
AgentsAlive int `json:"agents_alive"`
}
Verdict is Check's answer.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package loop is the implement loop's engine (itd-2609201916151817, spc-2609202134338445): the state file a run lives in, the checks that decide whether a run may start, and the step interface a driving host calls.
|
Package loop is the implement loop's engine (itd-2609201916151817, spc-2609202134338445): the state file a run lives in, the checks that decide whether a run may start, and the step interface a driving host calls. |