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.

Permission profile: repo-write — the agent can push commits, create branches, and open pull requests.

Pipeline

Context phase_0 Guardrails READY / BLOCKED Architect approval gate Executor TDD implement Reviewer loop (max 2) PR open pull request REQUEST_CHANGES

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.sh holding the repo's full test command.
  • guardrails_gate (harness-run, no LLM) runs that full suite once under timeout gate.timeoutSeconds. Exit 0 → READY; non-zero → BLOCKED with the log tail; a timeout → BLOCKED with "raise gate.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.sh and 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: APPROVED or VERDICT: REQUEST_CHANGES, which the runner parses (falling back to reviewer-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 this on an issue (classifier detects BUILD intent)
  • Slack: build cliftonc/repo#42
  • CLI: npm run cli -- build owner/repo#N
  • Issue label: applying ready-for-agent to an issue, in a repo the operator has opted into autonomy.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 to agent-building at 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 build on the same repo would not. The harness moves the label on again when the run ends — to ready-for-human on success, to agent-blocked on 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 by autonomy.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: auto has 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-impact is 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.