loop

package
v0.13.3 Latest Latest
Warning

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

Go to latest
Published: Oct 8, 2026 License: MIT Imports: 38 Imported by: 0

Documentation

Overview

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. `abcd build <itd-N>` starts a run; `abcd implement step` performs the next stage of the current lane and exits; `abcd implement receipt <path>` hands back what an agent stage waited on; `abcd implement status` renders the state. Nothing here holds a run in memory between invocations: every call reads the state file first, does at most one stage, writes the state file last and returns (decisions 1 and 2), so a killed process loses only the stage it was in, which the next call repeats.

The state lives in the checkout's local tier:

.abcd/.work.local/run/<run-id>/state.json   the run: its key, lanes, clock and record
.abcd/.work.local/run/.lock                 the advisory lock every mutation takes

The tier is never created here: only a repository abcd manages has it, which is what keeps a run managed-only without a check of its own to drift from (internal/core/mode draws the same line). The run directories beneath it are created one level at a time and proved real (fsutil.EnsureRealDirAll), and the state is written through the package's one atomic writer inside an os.Root, so a symlinked component is refused rather than followed.

The lane's stages are a sequence (Sequence), and each stage's body is a Handler the piece of the spec that delivers it registers in DefaultStages: the worktree (piece 6, lane.go), the brief (piece 5, brief.go), the implement stage and its receipt's verifier (piece 7, receipt.go), the validators (piece 8) and the landing (piece 9). A stage whose body this build does not carry is refused by name, with the piece that delivers it, and the run is left unchanged. A lane's files live in its own directory of the run:

.abcd/.work.local/run/<run-id>/<lane-id>/brief.md      the brief the loop renders
.abcd/.work.local/run/<run-id>/<lane-id>/receipt.json  the implementer's receipt

and its worktree in the machine-scoped store, ~/.abcd.noindex/worktrees/<root-sha>/<run-id>-<lane-id>. The process driver (piece 3, drive.go) is the same loop: Drive performs the next stage and, when it hands the lane to a role routed to a command-line runner (itd-2609201916056194), starts that agent through the runner and hands its receipt back through Receipt.

Core never writes to stdout; the CLI front door formats what these functions return.

Index

Constants

View Source
const (
	BriefFileName   = "brief.md"
	ReceiptFileName = "receipt.json"
	ReportFileName  = "report.md"
	DoDFileName     = "dod.log"
)

The files of a lane's directory, .abcd/.work.local/run/<run-id>/<lane-id>/. The loop writes the brief; the implementer writes the other three.

View Source
const (
	CheckKey           = "key"
	CheckReady         = "ready"
	CheckOpenQuestions = intent.StartCheckOpenQuestions
	CheckClaimSections = intent.StartCheckClaimSections
	CheckHold          = intent.StartCheckHold
	CheckBlocked       = intent.StartCheckBlocked
	CheckSteps         = intent.StartCheckSteps
	CheckPeers         = "peers"
	// CheckEligible is an issue key's row: the drained repository's own rule
	// takes the issue (itd-82 scope 2; decision 10 on the parent).
	CheckEligible = "eligible"
)

Check names, in the fixed order Check reports them.

View Source
const (
	// DrainStoppedCap: the drain opened --max lanes.
	DrainStoppedCap = "cap"
	// DrainStoppedEmpty: no eligible issue is left that this drain has not
	// taken.
	DrainStoppedEmpty = "empty"
)

Why a drain ended.

View Source
const (
	DrainLaneInProgress = "in-progress"
	// DrainLanePullRequest: the landing opened the lane's pull request and
	// armed it or left it open; the merge is a person's gate.
	DrainLanePullRequest = "pull-request"
	DrainLaneHandedBack  = "handed-back"
	// DrainLaneDone: the run is complete.
	DrainLaneDone = "done"
)

The outcomes of a lane the drain opened, as the drain reads its run.

View Source
const (
	// RoutePromoted: a user moment, promoted to an intent draft with
	// `capture promote`; the issue gains the draft in related_intents and
	// nothing else is written.
	RoutePromoted = "promoted"
	// RouteDecisionRecord: a rule about trust or safety, flagged as needing a
	// decision record with the question stated; nothing is minted.
	RouteDecisionRecord = "decision-record"
	// RouteRule: above the rule's severity, outside its categories, or held
	// by another of its rules; flagged naming the rule.
	RouteRule = "rule"
	// RouteHome: anything else, flagged with the home the decision belongs in.
	RouteHome = "home"
)

The routes a hand-back takes, by kind (scope 5).

View Source
const (
	StageRunner   = "runner"
	StageFallback = "fallback"
)

StageRunner is the record's stage for a role a runner ran, and StageFallback for one a runner did not.

View Source
const (
	// Remote is the remote a lane lands through: the one the default branch
	// is read from.
	Remote = "origin"
	// LandDirName is the lane's directory the landing writes into.
	LandDirName = "land"
	// PRBodyFileName is the pull request's body as the loop composed it, and
	// PRStrippedFileName the body re-read from the forge and stripped.
	PRBodyFileName     = "pr-body.md"
	PRStrippedFileName = "pr-body.stripped.md"
	// PreflightReceiptsRelDir is where the preflight mints its receipts, one
	// file named by the full commit id, in any worktree of the repository.
	PreflightReceiptsRelDir = TierRelDir + "/preflight-receipts"
	// RulesetsRelDir is the committed mirror of the default branch's rulesets.
	RulesetsRelDir = ".abcd/work/rulesets"
)

The landing's fixed names.

View Source
const (
	BundledWorkMinutes  = 120
	BundledPauseMinutes = 300
	BundledSubAgents    = 2
)

The bundled pace: 120 minutes of work, 300 of pause, two lanes (decision 5, the product thinker's numbers for this repository's runs on 2026-09-20). A repository that measured otherwise writes its own under `pace` in its .abcd/config.json.

View Source
const (
	HandBackUserVisible   = "user-visible"
	HandBackTrustRule     = "trust-rule"
	HandBackDesignFinding = "design-finding"
	HandBackSecondPackage = "second-package"
)

The kinds a lane hands an issue back as (itd-82: a reviewer's design finding, a second package, a user-visible change the remedy did not name, and a rule about trust or safety).

View Source
const (
	StageTranscript = "transcript"
	StageRecord     = "record"
)

StageTranscript is the run record's stage for a captured transcript, and StageRecord the refusal stage of the record's own verb.

View Source
const (
	HoldBeforePush = "push"
	HoldBeforeArm  = "arm"
)

The landing steps a hold stops before.

View Source
const (
	RoleRuthless = "ruthless-reviewer"
	RoleSecurity = "security-reviewer"
	RoleAuditor  = "intent-auditor"
)

The validators, by the agent definition each is started as.

View Source
const (
	ValidateDirName      = "validate"
	FixDirName           = "fix"
	ReturnFileName       = "return.md"
	VerdictFileName      = "verdict.json"
	AuditRequestFileName = "request.md"
)

The files of a validation round.

View Source
const BranchPrefix = "build/"

BranchPrefix is the namespace every lane branch is cut under.

View Source
const BundledFixRounds = 3

BundledFixRounds is the fix rounds a lane may take before it is handed back when nothing sets it (ruling DR1 of 2026-09-29: "tied to the pace settings, a per-run value set alongside --pace, default 3"; itd-50 decision 2).

View Source
const CheckRun = "run"

CheckRun is the exclusion of a planned intent this checkout already has a run in progress for: the pick starts a run, never resumes one.

View Source
const ConventionsFile = "AGENTS.md"

ConventionsFile is the file a brief's conventions are read from.

View Source
const DecisionsLogRel = ".abcd/work/DECISIONS.md"

DecisionsLogRel is the append-only decision log the brief quotes entries of.

View Source
const DrainSchemaVersion = 1

DrainSchemaVersion is the drain state's shape.

View Source
const DrainStateRel = RunRelDir + "/drain.json"

DrainStateRel is the drain's state file, beside the runs. Its name is not a run id, so the run listing never reads it as one.

View Source
const ReceiptSchemaVersion = 1

ReceiptSchemaVersion is the receipt's shape.

View Source
const RoleImplementer = "implementer"

RoleImplementer is the agent the implement stage hands a lane to.

View Source
const RunIDFamily = "run"

RunIDFamily is the run id's prefix; the id is minted through the record-id seam (adr-45), so two checkouts starting runs in one second draw distinct ids.

View Source
const RunRelDir = TierRelDir + "/run"

RunRelDir is the directory every run of this checkout lives under.

View Source
const SchemaVersion = 9

SchemaVersion is the state file's shape. A file carrying a version this build does not know is refused rather than read as this one: a field a later build added and this one would drop on its next write is a run silently losing state.

Version 2 added the run's pace (itd-2609201925079472). Version 1 is its strict subset, so a version-1 file is read as a run started before the loop paced a run: it carries no pace, runs unpaced, and is written back at version 2 by its next mutation. A version-1 file carrying a pace is not one version 1 wrote, and is refused.

Version 3 added the pick `abcd build next` made (itd-2609211116005482): the run's `pick` and a lane's `pick_sha`. Versions 1 and 2 are its strict subsets, read as runs no pick started and written back at version 3; one of them carrying a pick is not one its version wrote, and is refused.

Version 4 renamed the lane's stage (BU1, iss-2609291313276243): a lane's and a record line's `step` became `stage`, so the word "step" names only the spec's steps. Versions 1 to 3 wrote `step`, and are migrated on read: the read carries each `step` over to `stage` and writes nothing, and the run's next mutation writes the file back at version 4, as it does for the versions before. One of them that already says `stage` is not one its version wrote, and is refused.

Version 5 added the validate stage's record (spc-2609202134338445 piece 8): a lane's `validation`, its rounds and the verdicts the loop recorded. Version 4 is its strict subset, read as a run no validator has judged yet and written back at version 5; a version-4 file carrying a validation is not one version 4 wrote, and is refused.

Version 6 added the fix-round cap (ruling DR1, 2026-09-29): the pace's `fix_rounds`, and a lane's `hand_back` when it takes its cap of fix rounds without passing. Version 5 is its strict subset, read as a run started before the pace carried the cap, which runs on the bundled one, and written back at version 6; a version-5 file carrying either is not one version 5 wrote, and is refused.

Version 7 added the landing and the run record's transcripts (spc-2609202134338445 pieces 9 and 10): a lane's `landing`, the captures its receipts declared fixed (`resolves`) and the receipts it verified (`receipts`, each with the model its runner reported), and the run's `transcripts`. Version 6 is its strict subset, read as a run nothing has landed yet and written back at version 7; a version-6 file carrying any of them is not one version 6 wrote, and is refused.

Version 8 added the runner's record (itd-2609201916056194, spc-2609202134338445 piece 3): the run's `fallbacks`, one receipt per role a runner did not run, and the `route` a verified receipt or a validator's return names when a runner, not the host, ran its agent. Version 7 is its strict subset, read as a run the host ran every agent of and written back at the current version; a version-7 file carrying either is not one version 7 wrote, and is refused.

Version 9 made a run work in parallel up to its ceiling (ruling DR6, spc-2609202134341288): a lane's `awaits`, a list replacing the one `awaiting`, the run's `waiting`, a lane's `syncs` and `hold`, a pending step's `needs`, and the lane stages `held` and `discarded`. A file of version 8 or lower reads as a run whose lanes each await zero or one agent (its `awaiting` becomes a one-entry `awaits`) and is written back at version 9; a landing's `check_wait_since` (ruling DR6d-2) is version 9's too, added before any release wrote the version; one of them carrying anything only version 9 writes is refused, and so is a version-9 file carrying `awaiting`, which version 9 never writes.

View Source
const StageClaim = "claim"

StageClaim is the refusal stage of a start whose shared-run claim is refused for a reason of the caller's own (a session that has not joined, a bound).

View Source
const StageDrain = "drain"

StageDrain is the refusal stage of the drain run.

View Source
const StagePace = "pace"

StagePace is the refusal stage of a pace or ceiling the loop cannot run on.

View Source
const StagePick = "pick"

StagePick is the refusal stage of a pick that takes nothing.

View Source
const StateFileName = "state.json"

StateFileName is the name of a run's state file inside its directory.

View Source
const SyncDirName = "sync"

SyncDirName is the lane's directory a sync's brief and receipt live in.

View Source
const TierRelDir = ".abcd/.work.local"

TierRelDir is the local-ephemeral tier the run state lives in. It is never created here.

View Source
const VerdictUnachievable = "unachievable"

VerdictUnachievable is the verdict a lane is handed back with: the run's fix rounds could not bring it to a passing round (itd-50's UNACHIEVABLE).

Variables

Sequence is the lane's stages in the order the loop performs them: make the lane's worktree, render its brief, hand it to an implementer and take the receipt, run the validators, land it. A lane past its last stage is StageDone.

View Source
var WorktreeStoreRel = abcdhome.Rel("worktrees")

WorktreeStoreRel is the machine-scoped worktree store, relative to the caller's home.

Functions

func DrainSummaryLine added in v0.12.0

func DrainSummaryLine(r DrainRoute) string

DrainSummaryLine is a route in one line, for the text surfaces.

func LatestRun added in v0.12.0

func LatestRun(repoRoot string) (string, error)

LatestRun names the run a record read addresses when none is named: the one run in progress when there is one, else the most recently started run in this checkout. Several runs in progress are refused, naming them.

func LoadRunners added in v0.12.0

func LoadRunners(r layered.Roots) (*runner.Config, error)

LoadRunners reads the runner configuration (runner.Load) at the roots a lane starts from, and refuses in the loop's refusal shape on any fault, a model route its provider's allowlist does not admit included, before a run is created or a runner launched (itd-2609201916056194 criterion 5). The configuration's diagnostics are the caller's to print.

func Resolve

func Resolve(repoRoot, runID string) (string, error)

Resolve names the run a call addresses. An explicit id is checked for shape and presence; no id addresses the one run in this checkout that is not complete, and is refused naming them when there are several, or none.

func StateRelPath

func StateRelPath(runID string) string

StateRelPath is a run's state file, relative to the checkout root.

func StatusLanes added in v0.12.0

func StatusLanes(repoRoot string) ([]statusblock.Started, error)

StatusLanes is the state file read the status block's Now takes (itd-2609212103568351): one entry per run in progress, naming its intent and every lane of it alive (ruling DR6) — each lane's next stage and the roles it waits on, a held lane included — or, while no lane is alive and a spec step still waits, the run with its stage "pending". A complete run is not in a lane. An absent tier or run directory holds none. It is a statusblock.LaneReader.

func StatusPeers added in v0.12.0

func StatusPeers(repoRoot string) (statusblock.HeldBy, error)

StatusPeers is the peers read the status block's head takes (ruling CC1 of 2026-09-29): build next's own peers check, read once for the block and judged per intent, so the board's "next up" passes over exactly the intents another checkout holds that the pick passes over. It judges on behalf of no session, so every live claim is a peer's. A peer the listing cannot read fails closed on each record, as it does for the pick. It is a statusblock.PeerReader.

func ValidLaneID

func ValidLaneID(id string) bool

ValidLaneID reports whether id is a lane id the loop opens.

func ValidRunID

func ValidRunID(id string) bool

ValidRunID reports whether id is a run id this package mints.

Types

type AliveLane added in v0.13.0

type AliveLane struct {
	Lane     string  `json:"lane"`
	SpecStep int     `json:"spec_step"`
	Stage    Stage   `json:"stage"`
	Awaits   []Await `json:"awaits"`
	Hold     *Hold   `json:"hold,omitempty"`
	// Waiting is the lane's wait for its full check, with the time it began
	// (ruling DR6d-2); empty when it waits for none.
	Waiting string `json:"waiting,omitempty"`
}

AliveLane is one lane of a run with anything left, as a result reports it.

type AuditRun added in v0.12.0

type AuditRun struct {
	ReceiptID string `json:"receipt_id"`
	Request   string `json:"request"`
	// BaseSHA..HeadSHA is the whole delivery: from the base of the run's first
	// lane to this lane's head.
	BaseSHA string `json:"base_sha"`
	HeadSHA string `json:"head_sha"`
	// Worst, NotMet and Inconclusive are read from the verdict.
	Worst        string   `json:"worst,omitempty"`
	NotMet       []string `json:"not_met,omitempty"`
	Inconclusive []string `json:"inconclusive,omitempty"`
}

AuditRun is the fidelity audit the lane that closes the spec takes, once, over the whole delivery (ruling AI): the receipt the close parks for the same record, the request, the range it reads, and what the loop read from the verdict. The verdict itself is the return the run names; the close consumes it (piece 9).

type Await

type Await struct {
	// Role is the agent the host starts: an implementer, or a validator that
	// did not implement.
	Role string `json:"role"`
	// Brief is the file the agent is handed.
	Brief string `json:"brief"`
	// Receipt is where the agent writes its receipt; `implement receipt` is
	// called with this path.
	Receipt string    `json:"receipt"`
	Since   time.Time `json:"since"`
}

Await is what a lane waits on: the agent a host must start, the brief it is handed and the receipt it writes.

type CandidateSet added in v0.12.0

type CandidateSet struct {
	// Candidates pass every check, in the pick order.
	Candidates []intent.PickCandidate `json:"candidates"`
	// Excluded fail one, in record order.
	Excluded []Excluded `json:"excluded"`
}

CandidateSet is the pick's view of the planned intents.

type CheckResult

type CheckResult struct {
	Key    string     `json:"key"`
	Intent string     `json:"intent,omitempty"`
	Spec   string     `json:"spec,omitempty"`
	OK     bool       `json:"ok"`
	Checks []CheckRow `json:"checks"`
	// contains filtered or unexported fields
}

CheckResult is every pre-start check for one key.

func Check

func Check(repoRoot, key string) (CheckResult, error)

Check runs the pre-start checks for key against the checkout at repoRoot. It returns an error only for a fault in reading this checkout (the store will not load, git will not answer); a record that may not start is a result with OK false.

The rows, in order:

  • key: an intent id, or an issue id (decision 10's key). An issue key takes two rows after it and no others: eligible (the drained repository's own rule takes the issue, as `abcd drain` reads it) and peers (no peer holds it out of open/, and no session claims it).
  • ready: the implement-readiness gate (intent.Ready) — planned, criteria, the spec linked and written. Its advisory rows stay advisory here.
  • open_questions: no open question under `## Open Questions` (intent.StartChecksIn: every list item, less the settled markers).
  • claim_sections: no unanswered claim section — the mechanism prompt answered or the section absent, the scope conditions recorded.
  • hold: no `held:` on the record (iss-2609200830076665).
  • blocked: nothing the record names in `blocked_by` is unshipped (itd-2609211116005482: an intent with an unshipped blocker is not one a run may take); a superseded blocker is followed along `superseded_by` to its replacement, transitively (ruling BZ2 of 2026-09-29).
  • steps: the spec's `## Steps` reads, and leaves a step to build.
  • peers: no peer holds the record — no sibling worktree or local branch holds it in another bucket, and no session holds a live claim on it.

type CheckRow

type CheckRow struct {
	Name   string `json:"name"`
	OK     bool   `json:"ok"`
	Detail string `json:"detail"`
	Remedy string `json:"remedy,omitempty"`
	// contains filtered or unexported fields
}

CheckRow is one pre-start check's verdict.

type Context

type Context struct {
	RepoRoot string
	// RunDir is the run's directory, relative to RepoRoot.
	RunDir string
	State  State
	Now    time.Time
	// Await is the await a verifier is handed the receipt of; nil for a body.
	Await *Await
}

Context is what a stage's body is handed: where the checkout and the run live, the run as it stood before the stage, and the clock.

type DoDRun

type DoDRun struct {
	Command  string `json:"command"`
	ExitCode *int   `json:"exit_code"`
	// Output is the run's whole output, a path inside the lane's directory.
	Output string `json:"output"`
}

DoDRun is one run of the definition of done.

type DrainLane added in v0.12.0

type DrainLane struct {
	Issue    string    `json:"issue"`
	RunID    string    `json:"run_id"`
	OpenedAt time.Time `json:"opened_at"`
	Outcome  string    `json:"outcome"`
	PR       int       `json:"pr,omitempty"`
}

DrainLane is one issue the drain handed to a lane.

type DrainOptions added in v0.12.0

type DrainOptions struct {
	// Max is --max as typed; 0 when not given, which is all.
	Max int
}

DrainOptions are the drain's own flags.

type DrainResult added in v0.12.0

type DrainResult struct {
	State string `json:"state"`
	// Started is true when this invocation began a new drain.
	Started  bool     `json:"started"`
	Rule     string   `json:"rule"`
	Loosened []string `json:"loosened"`
	Order    string   `json:"order"`
	Max      int      `json:"max"`
	Pace     *Pace    `json:"pace"`
	// Lanes are every lane the drain opened, with its outcome as last read.
	Lanes []DrainLane `json:"lanes"`
	// Lane is the lane in progress after this call, if any.
	Lane *DrainLane `json:"lane"`
	// Start is the run this call started, when it opened a lane.
	Start *StartResult `json:"start"`
	// Routed are the hand-backs this call routed; HandBacks every one the
	// drain has routed; Flags the rule's hand-backs over the ledger as this
	// call read it, each naming its rule, written nowhere.
	Routed    []DrainRoute `json:"routed"`
	HandBacks []DrainRoute `json:"hand_backs"`
	Flags     []DrainRoute `json:"flags"`
	// Passed are eligible issues this call did not take, each with why.
	Passed         []Excluded             `json:"passed"`
	Dispositions   []capture.DrainVerdict `json:"dispositions"`
	NextEligibleAt *time.Time             `json:"next_eligible_at"`
	Stopped        string                 `json:"stopped,omitempty"`
	Complete       bool                   `json:"complete"`
	Next           string                 `json:"next"`
}

DrainResult is one drain invocation's summary.

func Drain added in v0.12.0

func Drain(repoRoot string, o Options, d DrainOptions) (DrainResult, error)

Drain performs one move of the drain run: it begins a drain when none is in progress, honours the window clock, routes the last lane's outcome, and opens the next eligible issue's lane, or says why it opens none. A drain without the repository's rule is refused before anything is written.

type DrainRoute added in v0.12.0

type DrainRoute struct {
	Issue string `json:"issue"`
	// From is "lane" for a lane's hand-back, "field" for the rule's.
	From  string `json:"from"`
	Kind  string `json:"kind"`
	Route string `json:"route"`
	// Draft is the intent draft a promotion made; Question the question a
	// decision-record flag states; Rule the rule a field hand-back names; Home
	// the home a flag names.
	Draft    string `json:"draft,omitempty"`
	Question string `json:"question,omitempty"`
	Rule     string `json:"rule,omitempty"`
	Home     string `json:"home,omitempty"`
	Reason   string `json:"reason"`
	// Wrote is the record change the route made, in words; "nothing" for a
	// flag.
	Wrote string     `json:"wrote"`
	At    *time.Time `json:"at,omitempty"`
}

DrainRoute is one hand-back and where it went.

type DrainState added in v0.12.0

type DrainState struct {
	SchemaVersion int       `json:"schema_version"`
	StartedAt     time.Time `json:"started_at"`
	UpdatedAt     time.Time `json:"updated_at"`
	// Rule is the decision record the rule was read from when the drain began.
	Rule string `json:"rule"`
	// Max is the cap on lanes the drain opens; 0 is all (the default).
	Max int `json:"max"`
	// Pace is the pace the drain started on; its window and pause bound the
	// drain as they bound a run.
	Pace            *Pace        `json:"pace"`
	WindowStartedAt *time.Time   `json:"window_started_at,omitempty"`
	NextEligibleAt  *time.Time   `json:"next_eligible_at,omitempty"`
	Lanes           []DrainLane  `json:"lanes"`
	HandBacks       []DrainRoute `json:"hand_backs"`
	// Stopped says why the drain ended, and EndedAt when; empty while it runs.
	Stopped string     `json:"stopped,omitempty"`
	EndedAt *time.Time `json:"ended_at,omitempty"`
}

DrainState is one drain, between invocations.

type Driver

type Driver string

Driver names what drives the loop. The host session is decision 5's default; the process driver is piece 3's, opt-in by configuration.

const DriverHost Driver = "host"

DriverHost is a host session calling `implement step` and `implement receipt` itself.

type Entry

type Entry struct {
	At   time.Time `json:"at"`
	Lane string    `json:"lane,omitempty"`
	// Stage is the stage the entry records: a lane stage, "start", "open" (a
	// later lane opened for its spec step) or "receipt".
	Stage string `json:"stage"`
	Note  string `json:"note,omitempty"`
}

Entry is one line of the run record.

type Excluded added in v0.12.0

type Excluded struct {
	ID     string `json:"id"`
	Check  string `json:"check"`
	Reason string `json:"reason"`
}

Excluded is a planned intent the pick did not consider, with the first check that excluded it.

type HandBack added in v0.12.0

type HandBack struct {
	At time.Time `json:"at"`
	// Kind, Reason and Home are set when the lane's own receipt handed the work
	// back (itd-82 scope 5): the kind of decision it found, what it found, and
	// where the decision belongs. Discarded is the lane's head the loop
	// discarded with its worktree and branch. The fields below are then empty.
	Kind      string `json:"kind,omitempty"`
	Reason    string `json:"reason,omitempty"`
	Home      string `json:"home,omitempty"`
	Discarded string `json:"discarded,omitempty"`
	// Verdict is VerdictUnachievable.
	Verdict string `json:"verdict,omitempty"`
	// Round is the round that did not pass once the cap was reached, and
	// FixRounds the cap the run held the lane to.
	Round     int `json:"round,omitempty"`
	FixRounds int `json:"fix_rounds,omitempty"`
	// Verdicts is the last round's verdicts as the loop recorded them, and
	// Findings the returns of the validators that did not pass, relative to
	// the checkout root.
	Verdicts string   `json:"verdicts,omitempty"`
	Findings []string `json:"findings,omitempty"`
	// NotMet and Undecided name the criteria the last audit judged not met and
	// could not decide, when the lane took the audit.
	NotMet    []string `json:"not_met,omitempty"`
	Undecided []string `json:"undecided,omitempty"`
}

HandBack is a lane stopped and handed back to the person, with what the last round found (ruling DR1 on itd-50's criterion 2).

type Handler

type Handler func(c Context, lane *Lane) (Outcome, error)

Handler performs one stage for a lane, writing what it made into lane. It must be idempotent: a process killed after the body's effect and before the state write runs the body again on the next call, so a body finds what it made last time rather than making it twice. An error leaves the state as it was; a *Refusal error is passed to the caller as the refusal.

type Hold added in v0.13.0

type Hold struct {
	Since    time.Time `json:"since"`
	Cause    string    `json:"cause"`
	Head     string    `json:"head"`
	Before   string    `json:"before"`
	Released bool      `json:"released,omitempty"`
}

Hold is a lane held after a sibling's hand-back (ruling DR6c): when, the handed-back lane that caused it, the head its passing round judged, and the landing step it stopped before (HoldBeforePush or HoldBeforeArm). Released is set once the person released it to land as it is.

type Landing added in v0.12.0

type Landing struct {
	// Closes is whether this lane's landing closes the spec (and ships the
	// intent): the lane that took the fidelity audit.
	Closes bool `json:"closes"`
	// Records is the loop's records commit on the lane's branch, and
	// RecordsDone is set once it is made, or once there was nothing to record.
	Records     string `json:"records,omitempty"`
	RecordsDone bool   `json:"records_done"`
	// PreflightReceipt is the preflight receipt that named the pushed head,
	// home-redacted, and Pushed the head the loop pushed.
	PreflightReceipt string `json:"preflight_receipt,omitempty"`
	Pushed           string `json:"pushed,omitempty"`
	// Body is the pull request's body as the loop composed it, relative to the
	// checkout root; PRURL is the pull request, and BodyChecked is set once the
	// body the forge holds was re-read and found clean.
	Body        string `json:"body,omitempty"`
	PRURL       string `json:"pr_url,omitempty"`
	BodyChecked bool   `json:"body_checked"`
	// Merge is the merge rule the ruleset gave, and Armed whether auto-merge
	// was armed by it. Nothing is pushed to the lane once Merge is set.
	Merge string `json:"merge,omitempty"`
	Armed bool   `json:"armed"`
	// Merged is the default branch's tip, as the remote held it, that carried
	// the pushed head when the landing cleaned the lane up.
	Merged string `json:"merged,omitempty"`
	// CheckWaitSince is when the landing began waiting for its full check: the
	// preflight receipt naming the head it pushes (ruling DR6d-2). It is set
	// by the first call that finds no receipt, kept until the push, and
	// cleared by it.
	CheckWaitSince *time.Time `json:"check_wait_since,omitempty"`
}

Landing is the land stage's progress on one lane.

type Lane

type Lane struct {
	// ID is the lane's name inside the run: lane-1, lane-2, ….
	ID string `json:"id"`
	// Key is the record the lane delivers: itd-N, or iss-N (decision 10).
	Key string `json:"key"`
	// SpecStep is the number of the spec step the lane lands, and StepTitle its
	// title. An unstepped spec is one implicit step, number 1.
	SpecStep  int    `json:"spec_step"`
	StepTitle string `json:"step_title"`
	// Stage is the next stage the loop performs for this lane; StageDone when the
	// lane has nothing left.
	Stage Stage `json:"stage"`
	// Awaits are the agents the lane waits on: one while its implementer
	// works, one per validator while its round is out. Each is a slot of the
	// run's ceiling until its receipt is verified (criterion 8, ruling DR6).
	Awaits []Await `json:"awaits,omitempty"`
	// Syncs are the merges of the default branch into the lane after a sibling
	// landed; Hold is set while the lane is held after a sibling's hand-back.
	Syncs []Sync `json:"syncs,omitempty"`
	Hold  *Hold  `json:"hold,omitempty"`
	// The lane's footprint, filled by the stages that make it.
	Branch   string `json:"branch,omitempty"`
	BaseSHA  string `json:"base_sha,omitempty"`
	HeadSHA  string `json:"head_sha,omitempty"`
	Worktree string `json:"worktree,omitempty"`
	Brief    string `json:"brief,omitempty"`
	Receipt  string `json:"receipt,omitempty"`
	PR       int    `json:"pr,omitempty"`
	// PickSHA is the record-only commit at the branch base carrying the run's
	// pick entry, on the lane a picked run commits it on; the receipt verifier
	// counts the implementer's commits from after it.
	PickSHA string `json:"pick_sha,omitempty"`
	// Validation is the validate stage's record (piece 8): one round per head
	// the validators judged, the last the current one. Only the loop writes a
	// verdict into it, parsed from the validator's own return (itd-58).
	Validation []ValidationRound `json:"validation,omitempty"`
	// HandBack is set when the lane was stopped and handed back to the person:
	// its Stage is then StageHandedBack.
	HandBack *HandBack `json:"hand_back,omitempty"`
	// Receipts are the implementers' receipts the loop verified for the lane,
	// the implement stage's and each fix round's, with the model each runner
	// reported (criterion 10).
	Receipts []ReceiptRecord `json:"receipts,omitempty"`
	// Resolves are the captures the lane's receipts declared fixed, each with
	// the lane's commit that fixed it; the landing resolves each (piece 9).
	Resolves []Resolution `json:"resolves,omitempty"`
	// Landing is the landing stage's progress (piece 9): each of its steps is
	// recorded as it completes, so a killed landing resumes at the step that
	// did not.
	Landing *Landing `json:"landing,omitempty"`
}

Lane is one lane: one spec step, one branch, one pull request.

func (Lane) CheckWait added in v0.13.0

func (l Lane) CheckWait() string

CheckWait is the lane's wait for its full check, as the status shows it, or "" when it waits for none: a landing whose push found no preflight receipt naming its head.

type LaneAwait added in v0.13.0

type LaneAwait struct {
	Lane  string
	Await Await
}

LaneAwait is one outstanding await with the lane it belongs to.

type LaneHandBack added in v0.12.0

type LaneHandBack struct {
	Kind   string `json:"kind"`
	Reason string `json:"reason"`
	Home   string `json:"home,omitempty"`
}

LaneHandBack is a lane's own hand-back: the kind of decision it found, the reason in a sentence, and, for the kinds routed to a home, where the decision belongs.

type LaneReceipt

type LaneReceipt struct {
	SchemaVersion int    `json:"schema_version"`
	RunID         string `json:"run_id"`
	Lane          string `json:"lane"`
	Branch        string `json:"branch"`
	// Commits are the full object names of the commits the implementer made on
	// the lane's branch.
	Commits []string `json:"commits"`
	// DefinitionOfDone is the repository's definition of done as the
	// implementer ran it.
	DefinitionOfDone *DoDRun `json:"definition_of_done"`
	// Report is the implementer's report, a path inside the lane's directory.
	Report string `json:"report"`
	// Model is the model the implementer's harness reported, as reported: the
	// binary cannot verify it.
	Model string `json:"model,omitempty"`
	// Resolves are the captures the lane fixed, each with the commit of the
	// lane that fixed it and the judgements a resolution records; the landing
	// resolves each with `capture resolve` (spec piece 9).
	Resolves []Resolution `json:"resolves,omitempty"`
	// HandBack stops the lane: the implementer found a decision inside the
	// work and hands it back rather than deciding it (itd-82 scope 5, the
	// `handback:` of decision 10 on the parent). The loop reads it before the
	// validators, discards the lane's work and ends the lane with it.
	HandBack *LaneHandBack `json:"handback,omitempty"`
}

LaneReceipt is the file an implementer writes when its lane is built.

type LaneWorktree

type LaneWorktree struct {
	// Home is the caller's home, the base the store is proved real from.
	Home string
	// RootSHA keys the store on the repository.
	RootSHA string
	// StoreRel is the lane's store directory, relative to Home.
	StoreRel string
	// Path is the worktree, Home/StoreRel/<name>.
	Path string
	// Branch is the lane's branch, build/<name>.
	Branch string
}

LaneWorktree is where a lane's worktree lives and what it is called.

type NextOptions added in v0.12.0

type NextOptions struct {
	// Max is --max as typed; 0 when not given.
	Max int
	// UntilEmpty is --until-empty.
	UntilEmpty bool
}

NextOptions are the run count the caller asked for.

type NextResult added in v0.12.0

type NextResult struct {
	CandidateSet
	Pick  intent.Pick `json:"pick"`
	Entry string      `json:"entry"`
	Start StartResult `json:"start"`
}

NextResult is what Next returns: the candidate set, the pick, the entry it writes, and the run it started.

func Next added in v0.12.0

func Next(repoRoot string, o Options, n NextOptions) (NextResult, error)

Next picks the readiest planned intent and starts its run. A run count past one is refused (criterion 5 is not built here); an empty candidate set is refused naming every excluded intent and its check, and writes nothing. The pick's entry is checked against the grounds writer's own gate before the run starts, so a pick never starts a run whose reason it cannot write.

type Options

type Options struct {
	// Now is the clock; nil means time.Now.
	Now func() time.Time
	// Minter mints the run id; the zero value is production.
	Minter recordid.Minter
	// Session is the joined session of the shared run state
	// (~/.abcd.noindex/runs/<root-sha>/) a new run is started for. When set, Start
	// claims the intent for it there, so a build of the same intent from any
	// other checkout of the repository sees the run before its lane has moved
	// or claimed anything (iss-2609252050506863). Empty, the run holds no
	// claim and is invisible to another checkout until its lane shows.
	Session string
	// Pace, SubAgents and FixRounds are the --pace, --sub-agents and
	// --fix-rounds flags as typed; nil when the flag was not given. They set a
	// new run's pace over every configured layer.
	Pace, SubAgents, FixRounds *string
	// Roots are where the pace's configuration layers are read; nil reads
	// them at layered.RootsFor(repoRoot).
	Roots *layered.Roots
}

Options are the seams tests set; the zero value is production.

type Outcome

type Outcome struct {
	Await *Await
	// HandBack stops the lane: it is handed back to the person with what the
	// stage found, and the loop starts nothing further for it (itd-50,
	// criterion 2). Set only with no Await.
	HandBack *HandBack
	// Stay records a step of a stage that takes several invocations (the
	// landing): the lane's changes are written and the record gets the note,
	// and the lane stays at the stage for the next invocation's step. Set only
	// with no Await and no HandBack.
	Stay bool
	// Goto sends the lane back to an earlier stage (the landing's sync sends
	// it to a fresh round); set only with no Await, HandBack or Stay.
	Goto Stage
	// Note is the run record's line for the stage.
	Note string
}

Outcome is what a stage's body returns. A body that hands its work to an agent returns Await: the lane then waits on the receipt it names and the stage completes only when Receipt verifies it.

type Pace added in v0.12.0

type Pace struct {
	WorkMinutes  PaceValue `json:"work_minutes"`
	PauseMinutes PaceValue `json:"pause_minutes"`
	SubAgents    PaceValue `json:"sub_agents"`
	FixRounds    PaceValue `json:"fix_rounds,omitzero"`
}

Pace is a run's pace: the working window, the pause after it, the ceiling on this run's lanes and validators alive at once, and the fix rounds a lane may take before it is handed back. FixRounds is zero in a run a state file before version 6 holds: it started before the pace carried the cap, and FixRoundCap reads the bundled value for it.

func (Pace) String added in v0.12.0

func (p Pace) String() string

String renders the pace and where each number came from, as the run record and the text surfaces name it.

type PaceValue added in v0.12.0

type PaceValue struct {
	Value  int    `json:"value"`
	Layer  string `json:"layer"`
	Origin string `json:"origin"`
}

PaceValue is one of the pace's numbers with the layer that supplied it: "flag", "repo", "machine" or "bundled", and the origin — the flag as typed, the file, or "bundled".

type PendingStep

type PendingStep struct {
	Number int    `json:"number"`
	Title  string `json:"title"`
	// Needs are the spec steps it waits for, as its `- needs:` line or the
	// default (every earlier step, ruling DR6b) resolved them; nil in a file
	// before version 9, which reads as the default.
	Needs []int `json:"needs"`
}

PendingStep is a spec step the run will open a lane for.

type ReceiptRecord added in v0.12.0

type ReceiptRecord struct {
	Role    string `json:"role"`
	Receipt string `json:"receipt"`
	// Model is the model the runner reported, as reported; empty when it
	// reported none. The binary cannot verify it.
	Model string `json:"model,omitempty"`
	// Route is the route that ran the agent when a runner ran it: the runner
	// asked for, the one that ran and the model it reported. Absent when the
	// host ran it, so a host-run receipt reads as it always has.
	Route *runner.RouteRecord `json:"route,omitempty"`
}

ReceiptRecord is one implementer's receipt the loop verified.

type RecordLane added in v0.12.0

type RecordLane struct {
	ID        string `json:"id"`
	Key       string `json:"key"`
	SpecStep  int    `json:"spec_step"`
	StepTitle string `json:"step_title"`
	Stage     Stage  `json:"stage"`
	Branch    string `json:"branch,omitempty"`
	BaseSHA   string `json:"base_sha,omitempty"`
	HeadSHA   string `json:"head_sha,omitempty"`
	// Receipts are the implementers' receipts the loop verified, each with the
	// model its runner reported.
	Receipts []ReceiptRecord `json:"receipts"`
	// Verdicts are every verdict the loop recorded, round by round.
	Verdicts []RecordVerdict `json:"verdicts"`
	// Resolves are the captures the lane fixed.
	Resolves []string `json:"resolves"`
	// PR is the lane's pull request, and Landing what its landing did.
	PR       int       `json:"pr,omitempty"`
	Landing  *Landing  `json:"landing,omitempty"`
	HandBack *HandBack `json:"hand_back,omitempty"`
}

RecordLane is one lane of the run record.

type RecordVerdict added in v0.12.0

type RecordVerdict struct {
	Round   int    `json:"round"`
	HeadSHA string `json:"head_sha"`
	Role    string `json:"role"`
	Verdict string `json:"verdict"`
	Pass    bool   `json:"pass"`
	// Return is the validator's return the verdict was parsed from.
	Return string `json:"return"`
	// Route is the runner route that ran the validator; absent when the host
	// ran it.
	Route *runner.RouteRecord `json:"route,omitempty"`
}

RecordVerdict is one verdict the loop recorded from a validator's return.

type Refusal

type Refusal struct {
	// Stage is where the loop refused: "check" before a run starts, "state" for
	// a run that cannot be read, "pause" for the window clock, or the lane stage
	// ("worktree", "implement", …) and "receipt" inside a run.
	Stage string `json:"stage"`
	// Check names the pre-start check that failed, when Stage is "check".
	Check string `json:"check,omitempty"`
	// Lane names the lane, inside a run.
	Lane   string `json:"lane,omitempty"`
	Reason string `json:"reason"`
	Remedy string `json:"remedy"`
	// Contention marks a refusal that is somebody else's hold rather than the
	// caller's fault — a peer holding the record, a run locked by another
	// invocation, a pause: back off and take other work. The CLI maps it to
	// exit 3, as `abcd implement` does.
	Contention bool `json:"contention,omitempty"`
	// Checks is every pre-start check's row, when Stage is "check", so a caller
	// sees the whole picture rather than the first failure.
	Checks []CheckRow `json:"checks,omitempty"`
	// Excluded is every planned intent a pick excluded and the check that
	// excluded it, when a pick found no candidate.
	Excluded []Excluded `json:"excluded,omitempty"`
	// contains filtered or unexported fields
}

Refusal is the one shape every refusal of the loop takes (criterion 13): the stage it happened at, the reason and the remedy, in text and in --json. A refusal writes nothing: the state file is as it was before the call, except for the time a landing began waiting for its full check (ruling DR6d-2), which the call that finds the wait writes once, whatever the call answers.

func AsRefusal

func AsRefusal(err error) (*Refusal, bool)

AsRefusal returns err's Refusal, if it carries one.

func (*Refusal) Error

func (r *Refusal) Error() string

Error renders the refusal as one line: stage, reason, remedy.

type Resolution added in v0.12.0

type Resolution struct {
	Issue   string `json:"issue"`
	Commit  string `json:"commit"`
	Note    string `json:"note"`
	Impact  string `json:"impact"`
	Grounds string `json:"grounds"`
}

Resolution is one capture a lane fixed: the issue, the lane's commit that fixed it, and what `abcd capture resolve` records — the note, the product impact and the grounds. The loop checks the shape and that the commit is one the receipt names; the capture store judges the rest when the landing resolves it.

type RunPick added in v0.12.0

type RunPick struct {
	// Lane is the lane whose branch carries the pick's entry as its first
	// commit: the run's first lane.
	Lane string `json:"lane"`
	// Entry is the grounds entry's text, written after the `pursued:` token.
	Entry string `json:"entry"`
	// Pick is the choice: the candidates in the pick order with their scores,
	// the rule, the runner-up.
	Pick intent.Pick `json:"pick"`
	// Excluded are the planned intents the checks excluded.
	Excluded []Excluded `json:"excluded"`
}

RunPick is the pick a run was started from, as the state carries it.

type RunRecord added in v0.12.0

type RunRecord struct {
	RunID     string    `json:"run_id"`
	Key       string    `json:"key"`
	Intent    string    `json:"intent"`
	Spec      string    `json:"spec"`
	Driver    Driver    `json:"driver"`
	Complete  bool      `json:"complete"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
	// Pace is the pace the run ran on, nil for a run started before the loop
	// paced a run.
	Pace        *Pace         `json:"pace"`
	Lanes       []RecordLane  `json:"lanes"`
	Pending     []PendingStep `json:"pending"`
	Transcripts []Transcript  `json:"transcripts"`
	// Fallbacks are the run's fallback receipts, and FallbackCounts their
	// count per runner asked for and per role (itd-2609201916056194
	// criterion 4).
	Fallbacks      []runner.FallbackReceipt `json:"fallbacks"`
	FallbackCounts runner.Counts            `json:"fallback_counts"`
	Record         []Entry                  `json:"record"`
}

RunRecord is a run's record as it is read at the end.

func CaptureTranscripts added in v0.12.0

func CaptureTranscripts(repoRoot, runID string, paths []string, capture TranscriptCapturer, o Options) (RunRecord, error)

CaptureTranscripts captures each path into the history store through capture, one capture per path, and records each in the run's state. It is refused on a run that is not complete: the record's transcripts are the run's, captured at its end. A capture that fails stops the call: the ones before it are recorded, and the refusal names the path that failed, so the call can be made again with the rest. A path captured before is captured again (the store's capture is idempotent) and recorded once.

func ReadRecord added in v0.12.0

func ReadRecord(repoRoot, runID string) (RunRecord, error)

ReadRecord reads a run's record.

type Runners added in v0.12.0

type Runners struct {
	// Config is the runner configuration read when the call began
	// (runner.Load); nil drives nothing and leaves every role on the host.
	Config *runner.Config
	// Transcripts is where each runner's transcript lands: abcd's own store
	// (runner.HistoryStore) in production.
	Transcripts runner.TranscriptStore
	// Timeout bounds one runner's run; runner.DefaultTimeout when zero.
	Timeout time.Duration
}

Runners is what the process driver starts a routed role through.

type Stage added in v0.12.0

type Stage string

Stage is one stage of a lane: the loop performs a lane's stages in order, and the lane as a whole lands one step of the spec (a spec's steps keep that word; BU1, iss-2609291313276243).

const (
	StageWorktree  Stage = "worktree"
	StageBrief     Stage = "brief"
	StageImplement Stage = "implement"
	StageValidate  Stage = "validate"
	StageLand      Stage = "land"
	StageDone      Stage = "done"
	// StageHandedBack is a lane that took its run's cap of fix rounds and still
	// did not pass (itd-50, criterion 2): the loop starts nothing further for
	// it, and its intent is the person's to replan.
	StageHandedBack Stage = "handed-back"
	// StageHeld is a lane whose round passed after a sibling was handed back
	// (ruling DR6c): it holds no slot, starts nothing and is never armed until
	// the person releases it (`implement step --release`) or discards it.
	StageHeld Stage = "held"
	// StageDiscarded is a held lane the person discarded: its pull request
	// closed, its worktree and branch removed, its step left unlanded.
	StageDiscarded Stage = "discarded"
)

The lane's stages, in the order Sequence performs them. StageDone is the state of a lane with nothing left to do, and StageHandedBack of a lane stopped and handed back to the person; neither is a stage with a body.

type StageDef added in v0.12.0

type StageDef struct {
	Name    Stage
	Piece   int
	Run     Handler
	Verify  Verifier
	Repeats bool
}

StageDef is one stage of the lane sequence: its name, the spec piece that delivers its body, and the body — nil when this build does not carry it.

A stage that hands the lane to several agents in turn (the validators, and the fresh implementer their findings go to) sets Repeats: a verified receipt then returns the lane to the stage's body rather than completing the stage, and the body decides, on the next step, whom it hands the lane to next or that the stage is complete.

type Stages added in v0.12.0

type Stages []StageDef

Stages is the lane sequence with its bodies.

func DefaultStages added in v0.12.0

func DefaultStages() Stages

DefaultStages is the lane sequence this build carries, each stage with the spec piece that delivers its body: the worktree (lane.go), the brief (brief.go), the implement stage with its receipt's verifier (receipt.go) and the validators with theirs (validate.go), and the landing (land.go).

type StartResult

type StartResult struct {
	RunID string `json:"run_id"`
	// State is the state file, relative to the checkout root.
	State string `json:"state"`
	// Resumed is true when a live run for the key already existed: the call
	// created nothing and names that run.
	Resumed bool          `json:"resumed"`
	Lane    Lane          `json:"lane"`
	Pending []PendingStep `json:"pending"`
	// Checks are the pre-start checks' rows when the call created the run; a
	// resumed start runs none, and carries none.
	Checks []CheckRow `json:"checks"`
	// Claim is the shared-run claim a new run took for Options.Session; nil when
	// no session was named or the start resumed a run, and then null in the
	// JSON, never absent, so the payload says the run holds no claim.
	Claim *implement.ClaimResult `json:"claim"`
	// Pace is the run's pace, each number with the layer that supplied it.
	// Null for a run started before the loop paced a run.
	Pace *Pace `json:"pace"`
	// HandBack is set when the run's lane stands handed back to the person.
	HandBack *HandBack `json:"hand_back,omitempty"`
	Next     string    `json:"next"`
}

StartResult is what Start returns.

func Start

func Start(repoRoot, key string, o Options) (StartResult, error)

Start resumes the live run for key, or runs the checks and, when every one passes, creates the run: the state file with one lane at the sequence's first stage, the spec's other unlanded steps pending, and the record's first line. A refused check writes nothing.

A live run for the same key in this checkout is looked up first, under the lock, and resumed — named, not duplicated, and not re-judged. The checks judged the record when the run was created; since then the run's own lanes change the tree they read (a lane's worktree moves the intent to shipped/, a lane claims it), so judging again would refuse the run as its own peer. So starting again after a kill loses nothing and repeats nothing, and the checks run only when a run is created. Only the key's shape is checked before the lookup, so a path is never built from a key that is not an intent id.

With o.Session named, a new run also claims its intent in the shared run state for that session, with the run id as the lane and the longest lease a claim takes: the peers check then counts that session's own live claim on the intent as its own, and a build from another checkout counts it as a peer's. A session that has not joined is refused before anything is created; a claim refused under the lock (a racing holder, a second session's bound) leaves no run behind; and a run whose state cannot be written releases the claim it took.

type State

type State struct {
	SchemaVersion int `json:"schema_version"`
	// RunID names the run and its directory.
	RunID string `json:"run_id"`
	// Key is the record the run was started for: an intent id, or an issue id
	// (decision 10), for which Intent and Spec are empty.
	Key string `json:"key"`
	// Intent and Spec are the intent the run delivers and the open spec it
	// builds against, as the readiness gate judged them.
	Intent string `json:"intent"`
	Spec   string `json:"spec"`
	// Driver is what drives the loop (DriverHost unless configured).
	Driver    Driver    `json:"driver"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
	// WindowStartedAt and NextEligibleAt are the window clock the pacing intent
	// (itd-2609201925079472) writes and reads. The loop honours NextEligibleAt:
	// before it, a stage is refused as a pause and nothing moves (decision 2).
	WindowStartedAt *time.Time `json:"window_started_at,omitempty"`
	NextEligibleAt  *time.Time `json:"next_eligible_at,omitempty"`
	// Pace is the pace the run started on, each number with the layer that
	// supplied it (itd-2609201925079472). Nil in a run a version-1 state file
	// holds: it started before the loop paced a run, and runs unpaced.
	Pace *Pace `json:"pace,omitempty"`
	// Pick is the pick `abcd build next` made to start the run: the
	// candidates, their scores and the grounds entry its first lane commits.
	// Nil for a run `abcd build <itd-N>` started.
	Pick *RunPick `json:"pick,omitempty"`
	// Lanes are the lanes opened so far, in the order they opened; several may
	// be open at once, up to the ceiling (ruling DR6). A lane lands one step of
	// the spec's `## Steps` (the whole spec when it lists none).
	Lanes []Lane `json:"lanes"`
	// Waiting is the work the ceiling holds back: each item's lane, role and
	// the time the ceiling first held it. An item leaves it when it takes a
	// slot, with a record entry naming the minutes it waited.
	Waiting []Waiting `json:"waiting,omitempty"`
	// Pending are the spec's unlanded steps no lane has been opened for yet.
	Pending []PendingStep `json:"pending"`
	// Record is the run record, accumulated as stages complete.
	Record []Entry `json:"record"`
	// Transcripts are the transcripts the run's record captured into the
	// history store once the run was complete, one capture per path (piece 10).
	Transcripts []Transcript `json:"transcripts,omitempty"`
	// Fallbacks are the run's fallback receipts, one per role a runner it was
	// routed to did not run (itd-2609201916056194 criterion 3): the role, the
	// runner asked for, the reason and the route that ran instead. The run's
	// summary counts them per runner and per role (runner.Tally).
	Fallbacks []runner.FallbackReceipt `json:"fallbacks,omitempty"`
}

State is one run: everything the loop needs between two invocations.

func ReadState

func ReadState(repoRoot, runID string) (State, error)

ReadState is the state file's one reader. It refuses a run id of the wrong shape before building a path from it, reads through the checkout's os.Root (so a symlinked component cannot walk the read out of the checkout), decodes strictly — an unknown field is a file some other writer produced — and holds the file to the version and the id it is stored under.

func Runs

func Runs(repoRoot string) ([]State, error)

Runs lists this checkout's runs, oldest first. An absent tier or run directory holds none. A state file that cannot be read fails the listing, naming it: a run the loop cannot read is not one it may skip past.

func (State) Ceiling added in v0.13.0

func (s State) Ceiling() int

Ceiling is the most agents the run may have out at once.

func (State) Complete

func (s State) Complete() bool

Complete reports whether the run has nothing left: every lane is done and no spec step is waiting for one.

func (State) FixRoundCap added in v0.12.0

func (s State) FixRoundCap() int

FixRoundCap is the fix rounds a lane of the run may take before it is handed back: the run's own, or the bundled value for a run that started before the pace carried one (an unpaced run, or a state file before version 6).

func (State) Issue added in v0.12.0

func (s State) Issue() string

Issue is the issue an issue-keyed run fixes (decision 10), or "" for a run that builds an intent.

func (State) SlotsInUse added in v0.13.0

func (s State) SlotsInUse() int

SlotsInUse is the agents the run has out: its outstanding awaits.

type StepResult

type StepResult struct {
	RunID string `json:"run_id"`
	Lane  string `json:"lane,omitempty"`
	// PerformedStage is the stage this call completed; empty when it completed none
	// (the lane awaits a receipt, or the run is complete).
	PerformedStage Stage `json:"performed_stage,omitempty"`
	// Stage is the lane's next stage after the call.
	Stage Stage `json:"stage,omitempty"`
	// Awaiting is what the lane waits on, when it waits on an agent.
	Awaiting *Await `json:"awaiting,omitempty"`
	Complete bool   `json:"complete"`
	// NextEligibleAt is set when this call closed the run's window: nothing
	// was performed, and no stage is taken before this time.
	NextEligibleAt *time.Time `json:"next_eligible_at,omitempty"`
	// HandBack is set when the lane was handed back to the person: this call
	// stopped it, or it stood stopped when the run was started again.
	HandBack *HandBack `json:"hand_back,omitempty"`
	// Slots is the agents the run has out after the call, and Ceiling the most
	// it may have (the run's pace.sub_agents); CeilingReached is set when this
	// call found every slot taken and handed nothing out.
	Slots          int  `json:"slots"`
	Ceiling        int  `json:"ceiling"`
	CeilingReached bool `json:"ceiling_reached,omitempty"`
	// Alive is every lane of the run with anything left, with its stage and
	// each agent it awaits, the held lanes included.
	Alive []AliveLane `json:"alive,omitempty"`
	// Blocked are the landings that waited on the forge's merge this call
	// while another lane moved: each holds only its own lane. Any other refused
	// stage is the call's answer, and no lane moves.
	Blocked []Refusal `json:"blocked,omitempty"`
	// Route is the route that ran the agent when the process driver started
	// it through a runner (Drive); absent when the host is to run it.
	Route *runner.RouteRecord `json:"route,omitempty"`
	// Fallback is the fallback receipt this call recorded when the runner a
	// role is routed to did not run it and the host is handed the role.
	Fallback *runner.FallbackReceipt `json:"fallback,omitempty"`
	Next     string                  `json:"next"`
	// contains filtered or unexported fields
}

StepResult is what advance and Receipt return.

func Discard added in v0.13.0

func Discard(repoRoot, runID, laneID string, o Options) (StepResult, error)

Discard does not land a held lane, on the person's word (`implement step --discard <lane>`): its worktree is removed from the machine store and its branch deleted, then its pull request is closed if it opened one, and its stage is `discarded`; its step stays unlanded in the spec, so a replanned remainder carries it. The local removals come first, so a refused one leaves the pull request open and the lane held, and a retry finds nothing half done: a worktree or branch already gone is passed over. It changes nothing when the lane is not held or any lane is still at work.

func Drive added in v0.12.0

func Drive(ctx context.Context, repoRoot, runID string, steps Stages, o Options, rs Runners) (StepResult, error)

Drive performs the next stage as advance does and, when the stage hands the lane to an agent whose role is routed to a runner, starts that agent through the runner and hands its receipt back through Receipt. A step that re-tells an await an earlier call began starts nothing. A role on the host, or a runner that did not run it, returns the await for the host to act on, as advance returns it; the latter also names the fallback it recorded.

func Receipt

func Receipt(repoRoot, runID, receiptPath string, steps Stages, o Options) (StepResult, error)

Receipt hands back the receipt an agent stage waited on. The path is looked up among every outstanding await of the run, not only one lane's, and the lane that await belongs to is advanced (ruling DR6): a verified receipt frees its slot, which the next `implement step` fills. It is refused when no await names the path, when this build carries no verifier for the stage, and when the verifier refuses it; in every refusal the run stays where it was and the agent's slot stays taken.

func Release added in v0.13.0

func Release(repoRoot, runID, laneID string, o Options) (StepResult, error)

Release lands a held lane as it is, on the person's word (`implement step --release <lane>`): its stage returns to `land` and its landing resumes at the step it stopped before, synced first when a sibling landed since its base. It changes nothing when the lane is not held or any lane is still at work.

type Sync added in v0.13.0

type Sync struct {
	At         time.Time `json:"at"`
	Siblings   []string  `json:"siblings"`
	Merged     string    `json:"merged"`
	Conflicted bool      `json:"conflicted"`
	Paths      []string  `json:"paths,omitempty"`
	Brief      string    `json:"brief,omitempty"`
	Receipt    string    `json:"receipt,omitempty"`
	// Head is the head the sync produced: the merge commit, or the verified
	// head of the implementer who resolved the conflict; empty until then.
	Head string `json:"head,omitempty"`
	// Round is the fresh round that judges Head, once it has opened.
	Round int `json:"round,omitempty"`
}

Sync is one merge of the default branch into a lane after a sibling lane of the run landed (spc-2609202134341288, "Two lanes that touch the same files"): the siblings whose landing caused it, the default branch's sha merged in, whether it conflicted, and the head it produced. A conflicting sync goes to a fresh implementer with the brief and receipt named here.

type Transcript added in v0.12.0

type Transcript struct {
	At time.Time `json:"at"`
	// Path is the transcript as it was handed to the capture, home-redacted.
	Path string `json:"path"`
	// Session is the session the history store recorded it under, and Stored
	// where it lives there, home-redacted.
	Session string `json:"session"`
	Stored  string `json:"stored"`
	// Wrote is false when the store already held it (an idempotent capture).
	Wrote bool `json:"wrote"`
	// ScanGap is the capture's scan gap, home-redacted: the repository armed a
	// scanner augmenter (gitleaks) that did not run, so the transcript was
	// stored masked by the native scanner alone. Empty when there is none.
	ScanGap string `json:"scan_gap,omitempty"`
}

Transcript is one transcript the run record captured into the history store.

type TranscriptCapturer added in v0.12.0

type TranscriptCapturer func(path string) (Transcript, error)

TranscriptCapturer captures one transcript by path into the history store, as `abcd history capture <path>` does, and reports what it stored.

type ValidationRound added in v0.12.0

type ValidationRound struct {
	Round int `json:"round"`
	// HeadSHA is the lane's head the round's validators judge.
	HeadSHA    string         `json:"head_sha"`
	Validators []ValidatorRun `json:"validators"`
	// Fix is the verified receipt of the fresh implementer the round's
	// findings were handed to; once set, the next step opens the next round.
	Fix string `json:"fix,omitempty"`
}

ValidationRound is one round of the validate stage: the validators it hands the lane's head to, one at a time, and the fresh implementer it hands their findings to when one of them did not pass.

type ValidatorRun added in v0.12.0

type ValidatorRun struct {
	Role   string `json:"role"`
	Brief  string `json:"brief"`
	Return string `json:"return"`
	// Verdict is the verdict the loop parsed from the return; empty until the
	// return is handed back. Pass is whether it lets the lane advance.
	Verdict string `json:"verdict,omitempty"`
	Pass    bool   `json:"pass"`
	// Audit is the fidelity audit's request and reading, on the
	// intent-auditor's run.
	Audit *AuditRun `json:"audit,omitempty"`
	// Route is the route that ran the validator when a runner ran it; absent
	// when the host ran it. It is the one field a runner-run review's record
	// differs in from a host-run one's (itd-2609201916056194 criterion 7).
	Route *runner.RouteRecord `json:"route,omitempty"`
}

ValidatorRun is one validator of a round: the fresh agent handed the lane, its brief, the return it writes, and the verdict the loop parsed from that return.

type Verifier

type Verifier func(c Context, lane *Lane, receipt string) error

Verifier checks the receipt an agent stage waited on. An error refuses the receipt and the lane stays where it was.

type Waiting added in v0.13.0

type Waiting struct {
	Lane  string    `json:"lane"`
	Role  string    `json:"role"`
	Since time.Time `json:"since"`
}

Waiting is one piece of work the ceiling held back.

Jump to

Keyboard shortcuts

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