Change stacks and decisions
A change stack is a run's diff arranged for review. A decision is a choice an agent made during a run, drawn from what it said. A station turns each on in its station fields:
| Station field | Effect |
|---|---|
diffs: true | After each run, a change stack is drafted for each checkout that changed. |
check | Each change stack drafted at the station is checked with this command, so it has effect only with diffs: true. With both set, every repository of the line must have setup. |
decisions: true | After each completed run, decisions are drafted from it. |
Both are drafted by Spotlight after the run, not by the agent. Both need the
deployment to have drafting set up; where it does not, a change stack is
refused with Change stacks are not set up on this deployment., and
no decisions are drafted.
Reviewing an agent's changes
gives the reasons.
Change stacks
States
| State | Meaning |
|---|---|
drafting | The diff is received and the stack is being drafted. |
ready | Every change in the diff is placed in exactly one layer. |
failed | Drafting did not finish. The stack carries the reason. |
| Message | Cause |
|---|---|
Spotlight couldn't draft the change stack. | Every drafting attempt failed. |
Drafting was interrupted before it finished. | Drafting stopped without a reason. |
Drafting is attempted at most 4 times. Each attempt may take 30 minutes, and the wait before the next starts at 30 seconds and doubles. A diff is at most 6 MiB and 2,000 changes.
Structure
A stack is an ordered list of cohorts. A cohort is an ordered list of layers.
| Part | Holds |
|---|---|
| Cohort | A kind, a title and an intent. |
| Layer | A title, a summary, the changes it places, a note on each file it changes, and the earlier layers it depends on with the reason for each. |
| Cohort kind | Holds |
|---|---|
change | The changes a reviewer reads, in layers that build on one another. |
incidental | Lockfiles, generated files, test snapshots, pure renames and formatting-only changes. Spotlight places these itself. |
A diff whose changes are all incidental gives a ready stack at once.
Languages read across files
Before drafting, Spotlight reads the changed files in these languages, before and after the change. It reads only the lines the diff shows, the changed lines and their context, and leaves the other lines blank. Binary files are not read. Each side of a file is read only when that text is at most 524,288 characters.
Spotlight links each change, a hunk of the diff, to the hunks that define a name it uses or imports, or a route it names. The hunks may be in the same file. A hunk is not linked to itself. Route links come only from the after side.
| Language | Files |
|---|---|
| TypeScript | .ts, .mts, .cts, .tsx |
| JavaScript | .js, .mjs, .cjs, .jsx |
| Vue | .vue: the script blocks, and the component tags of the template |
| Svelte | .svelte: the script blocks, and the component tags of the markup |
| Python | .py |
| Go | .go |
| Java | .java |
| Ruby | .rb, .rake |
| Rust | .rs |
| PHP | .php |
| C# | .cs |
| Terraform | .tf |
Routes are read in every language above but Terraform:
- from a call whose first argument starts with
/, where the call is named for an HTTP method (get,post,put,patch,delete,head,options) or isall,any,route,handle,handlefuncormatch, in TypeScript, JavaScript, Vue, Svelte, Python, Ruby, Rust and Go; - from Nest decorators, with the controller's prefix, in TypeScript, JavaScript, Vue and Svelte;
- from route annotations in Java, attributes in PHP and C#,
Route::and other route calls in PHP, andMapcalls such asMapGetin C#; - in Go, also from a pattern that starts with its method, as in
"GET /tasks/{id}", given toHandle,HandleFunc,route,match,anyorall, with or without a host before the path.
A route links only when its path has at least 2 segments that are not wildcards.
Checks
A station's check command is run against the stack in a sandbox of its own,
from the line's runner image, on a runner that meets the run's placement.
| Step | Checked tree |
|---|---|
| The whole change | The run's diff applied in full. Always first. |
Each change layer, in order | The incidental changes and every layer up to and including this one. |
- Each checked tree borrows git objects from the host's mirror of the repository, when the host has one.
- Each repository's
setupruns before the first step, and again before any step whose lockfile changes differ from the step before. - When the whole change fails, the layers are not checked.
- When the whole change passes and a layer fails, the stack is redrafted once and checked again.
- A step may take 5 minutes, and a check 60 minutes in all. The last 16,000 characters of each step's output are kept.
- A runner holds a check under a 10-minute lease, which it renews while the
check runs, with
POST /api/runs/{runId}/change-stack-checks/{stackId}/lease. A renewal is refused 65 minutes after the claim. The hold ends when the lease lapses, or 65 minutes after the claim. - A check is claimed at most 3 times.
- A check that has waited more than 60 minutes is skipped with
nobody-checkedwhile no runner holds it.
| Check state | Meaning |
|---|---|
waiting | Waiting for a runner to claim and run it. |
checked | Every step ran. Each step passed or failed. |
skipped | The check did not run. It carries a skip code. |
| Code | Cause |
|---|---|
no-base | Spotlight doesn't know the commit where the run began. The layers can't be checked without it. |
no-checkout | Spotlight does not know which checkout the changes are from. |
checkout-failed | Spotlight could not lay out a copy of the checkout to check. |
setup-failed | The setup of the checkout failed before the check ran. |
no-room | The runner had no room to check the layers. |
out-of-time | There was no time left to check the layers. |
no-image | The host couldn't pull the runner image that the line names. |
nobody-checked | No runner checked the layers in time. The check had waited more than 60 minutes, and no runner held it. |
unprovisioned | Spotlight couldn't provision a runner to check the layers. |
A push pool receives a check as provision work of kind check. See
Provision call.
Where a change stack is shown
- The run's change stack page in the web app, reached from the task's "Changes" property. See Web app.
- A comment on the task's pull request, kept up to date with the latest ready stack, when the workspace is connected to GitHub.
Decisions
Kinds
| Kind | Label in the pull request comment |
|---|---|
trade-off | Trade-off |
assumption | Assumption |
limitation | Limitation |
rejected-approach | Rejected approach |
deviation | Deviation |
Fields
| Field | Type | Description |
|---|---|---|
kind | string | One of the kinds above. |
title | string | At most 120 characters. |
why | string | The reasons, at most 2,000 characters. |
alternatives | array of strings | What was weighed and set aside. At most 5, each at most 300 characters. |
paths | array of strings | The files the decision bears on. 1 to 20. |
steps | array of integers | The steps of the run the decision is drawn from. 1 to 20. |
disputes | array of objects | Each has the file, the person and the reason. |
A drafted decision that breaks these limits is dropped.
Drafting
- Decisions are drafted after a run completes at a station with
decisions: true, unless the task reviews a pull request. - A task has one set of decisions. Each later drafting replaces it.
- Drafting is attempted as for a change stack. When every attempt fails, the task keeps the decisions it had.
Standing
| Standing | Meaning |
|---|---|
proposed | Drafted, and open to disputes. Later runs may replace it. |
stands | The task's pull request merged. Nothing is drafted for the task again. |
When the pull request is closed without merging, the decisions are discarded.
Decisions that stand are listed by
GET /api/workspaces/{slug}/decisions, filtered by repository, path,
since and until (merge times).
Disputes
A member of the task's team disputes a decision by replying to it on one of its files in the change stack page. A reason is 1 to 1,000 characters.
Where decisions are shown
- The change stack page, beside the files each decision bears on.
- A comment on the task's pull request, when the workspace is connected to GitHub. It is withdrawn when the task has no decisions.
- The briefing of each later run on the task, under
## Decisions on record, with each dispute. A run at a station withdecisions: trueis also briefed under## Say what you decide. See Briefing.