Docs
Build
The build workflow is Last Light's most complex workflow. It runs a full software development cycle: check prerequisites, plan the implementation, write the code, review it independently, fix any issues, and open a pull request. Every step runs in an isolated Docker sandbox with a scoped GitHub token.
repo-write — the agent
can push commits, create branches, and open pull requests.
Pipeline
Phases
1. Guardrails
Pre-flight check that verifies the repo has foundational tooling: a test framework, linter, and type checker. It runs in two parts:
guardrails(agent) installs dependencies, runs typecheck and lint, and writes.git/lastlight-gate.shholding the repo's full test command.guardrails_gate(harness-run, no LLM) runs that full suite once undertimeout gate.timeoutSeconds. Exit 0 →READY; non-zero →BLOCKEDwith the log tail; a timeout →BLOCKEDwith "raisegate.timeoutSeconds".
READY requires the full suite to pass — the
exit code decides, so a subset or "representative" run is never evidence.
The report records the command and how long it took, so you can size
gate.timeoutSeconds from real numbers (see
Making your repo agent-friendly).
If blocked, the workflow stops unless the issue has a
lastlight:bootstrap label (used for issues that set up the
missing tooling).
2. Architect
Clones the repo, reads the issue context, and produces a detailed
implementation plan at .lastlight/issue-N/architect-plan.md.
Includes problem statement, files to modify with line numbers, and a
step-by-step approach.
If the post_architect approval gate is enabled, the workflow
pauses here until a maintainer comments @last-light approve
or approves via the dashboard. The pause message links to a
focused approval view for the architect plan — in
server storage mode the maintainer can edit and save the plan before
approving; in repo mode it links out to the doc on GitHub.
3. Executor
Implements the architect's plan using TDD: writes failing tests first,
then implements until tests pass. Runs the targeted tests for touched
areas plus typecheck, lint and build, then the full suite
once within gate.timeoutSeconds, judged by
exit code. A timed-out suite is reported in the executor summary rather
than retried — CI runs the full suite on the PR. Pushes to the workflow's
branch.
4. Reviewer
Independent code review with no shared context from the
executor. Reads only the diff and the plan. Outputs a verdict:
APPROVED or REQUEST_CHANGES.
On REQUEST_CHANGES, the runner enters a fix loop: the executor
addresses the feedback, then the reviewer re-reviews. This repeats up to
2 cycles. The post_reviewer approval gate can pause before
each fix cycle.
5. PR
Creates a pull request from the workflow branch to main. The PR body includes the architect's plan, the reviewer's verdict, and links to the issue.
How phases hand off
Every phase is a fresh agent session, so nothing is carried over in memory. Phases coordinate through the branch — the code and tests they commit — and a folder of plain-text build documents keyed by the issue:
.lastlight/issue-42/
guardrails-report.md ← install/typecheck/lint results, the gate command and how long it took
architect-plan.md ← problem statement with file:line evidence, files to change, test strategy
status.md ← current phase, guardrails and reviewer status, loop counters
executor-summary.md ← files changed, actual test/lint output, deviations from the plan
reviewer-verdict.md ← the verdict, the issues found, and what was run - Guardrails → Architect. The report tells the architect which verification commands really exist, so the plan can rely on them. The gate script itself lives in
.git/lastlight-gate.shand is never committed. - Architect → Executor. The executor treats the plan as its ground truth instead of re-deriving intent from the issue body.
- Executor → Reviewer. The reviewer reads the diff against the base branch, the plan and the executor's summary: the code and the claims about it, never the executor's reasoning.
- Reviewer → fix loop. The reviewer's final message starts with
VERDICT: APPROVEDorVERDICT: REQUEST_CHANGES, which the runner parses (falling back toreviewer-verdict.md). Requested changes go to a fix session that addresses only the reported issues.
By default these documents are committed on the build branch. Set
buildAssets.location: server to keep them out of the target
repository entirely: they are stored on the Last Light server, staged into
each phase and collected afterwards, and the links in comments point at
the dashboard instead of GitHub. See
Configuration.
Triggers
- GitHub comment:
@last-light build thison an issue (classifier detects BUILD intent) - Slack:
build cliftonc/repo#42 - CLI:
npm run cli -- build owner/repo#N - Issue label: applying
ready-for-agentto an issue, in a repo the operator has opted intoautonomy.repos. This is the software-factory pipeline: the label is the source of truth for where an issue has got to, and the harness advances it toagent-buildingat dispatch. The allow-list ships empty, so labelling does nothing until a deployment opts a repository in — and a labelled build runs behind the stage's approval gates (post_architect,post_reviewer) even where a@last-light buildon the same repo would not. The harness moves the label on again when the run ends — toready-for-humanon success, toagent-blockedon a failure or a cancel — and a backstop cron (pick-up-ready-issues, every 20 min) picks up any issue whose label event was never delivered. Autonomous dispatches are bounded byautonomy.budget: a harness-wide concurrency ceiling, a per-repo builds-per-day quota, and daily spend limits, all checked before a run starts and all recorded when they refuse. See Autonomous mode. - Merging: by default the PR parks for a human. A stage configured
on_merge: autohas the agent enable GitHub auto-merge on the PR it opened — CI and branch protection still gate the landing, and the agent never merges directly. That applies to pipeline runs only: a human-triggered build always parks the PR. (auto-low-impactis accepted but not yet implemented, and behaves as the default.)
Model configuration
Each phase can use a different model via the LASTLIGHT_MODELS
env var (the agent runtime is agentic-pi,
provider-agnostic; default anthropic/claude-sonnet-5-5). Common
setup: a strong reasoning model for architect (planning quality), a mid-tier
model for executor and reviewer (speed), and a small/cheap model for
guardrails (cost).
LASTLIGHT_MODELS='{"architect":"openai/gpt-5.5","guardrails":"anthropic/claude-haiku-5-5"}' Or use a single OpenRouter key to mix vendors freely from one billing surface — useful if you don't want to manage separate OpenAI and Anthropic accounts:
LASTLIGHT_MODELS='{"architect":"openrouter/anthropic/claude-sonnet-4.5","guardrails":"openrouter/google/gemini-2.5-flash"}'
Per-phase reasoning effort goes through LASTLIGHT_THINKING /
LASTLIGHT_THINKINGS — typical setup is high for
architect and reviewer, minimal for triage / screen.