Docs
Overview
Last Light ships with a roster of default workflows, grouped by family:
build; question-answering (answer,
issue-comment, pr-comment); triage;
review (pr-review); fix (pr-fix);
reporting (repo-health); and security
(security-review, security-feedback) — several with optional cron variants.
Each is a YAML file in workflows/ that the runner executes
phase-by-phase. The harness itself knows nothing about "build" or "triage"
specifically — it just runs whatever phases a workflow declares.
Workflows fall into three categories based on complexity:
Multi-phase workflows
These run multiple agent sessions in sequence, often with loops and approval gates. Each phase runs in an isolated Docker sandbox with a shared workspace so files persist between phases.
Full Architect → Executor → Reviewer cycle. Plans, implements, reviews, and opens a PR.
Socratic Q&A loop that shapes half-formed ideas into detailed specs through conversation.
Single-phase workflows
Each runs one focused agent session — fast, cheap, and targeted.
Classifies, labels, and responds to new or reopened issues.
Conversational reply when a maintainer comments without requesting a build.
Researches docs + web and posts a single sourced answer when an issue or message is a question.
Answers a maintainer's question about an open PR with concrete, code-cited evidence.
Reads a PR diff and posts line-anchored inline review comments. Read-only — no code modifications.
Lightweight diagnose-then-fix-and-push — no architect or reviewer. Classifies why the PR is red first, then fixes it and pushes, or stops if nothing here can repair it.
Fixes a dependency-update PR whose checks failed — diagnoses the breakage first, brings the branch up to date, pushes the smallest fix, and hands the merge decision to dependabot-pr-merge once CI is green. Via webhook and a daily per-PR cron backstop.
Finds already-green dependency-update PRs and enables auto-merge on the safe ones — including a major bump whose impact is low enough — via webhook and a daily per-PR cron backstop.
Weekly health report — open/stale issues, PR velocity, action items.
Scans a repo with npm audit, semgrep, gitleaks + AI review and files one grouped summary issue per run.
Acts on maintainer feedback to a security issue — split into issues, accept risk, false-positive, or reopen.
Tests a behaviour claim in the sandbox and reports CONFIRMED / REFUTED / INCONCLUSIVE with evidence.
Drives a CLI or locally-served app through a flow and reports step-level pass/fail with evidence.
Records a short demo video of a PR/feature — drives the web UI in a headless browser, composites a titled mp4 with ffmpeg, and embeds it inline.
Direct chat
Not a YAML workflow — Slack messages that don't match any of the above
intents are handled by the chat skill, which runs an
in-process @earendil-works/pi-ai chat loop (no sandbox spawn)
for low-latency replies. Each Slack thread maps to one persistent pi-ai
conversation, rehydrated from the messaging_messages SQLite
table on every turn, so context holds across messages.
The LLM classifier routes messages to chat when the intent is conversational — questions, discussion, status checks, and anything that isn't explicitly a build, explore, triage, review, or approval.
How workflows are triggered
| Trigger | Workflows | How it works |
|---|---|---|
| GitHub webhook | issue-triage, pr-review, build, pr-fix, dependabot-ci-fix, dependabot-pr-merge, issue-comment, answer, pr-comment, security-feedback, verify, qa-test, demo | GitHub App sends events to the harness. The router + LLM classifier decides which workflow to run. A newly-opened issue classified as a question routes to answer; a comment on a PR routes to pr-comment; an @last-light mention on a security-labelled issue routes to security-feedback. A PR whose CI fails (a check_suite failure) is classified and routes to dependabot-ci-fix when it's a dependency update; a dependency PR whose CI passes (a check_suite success) routes deterministically to dependabot-pr-merge. Under review.trigger: after-checks, a settled check suite on any other PR routes to pr-review; adding the review.requestLabel, requesting a review from the bot, or pressing Re-run on the last-light/review check all do the same. @last-light verify <claim> / qa-test <target> / demo <notes> (or the equivalent classifier intent) route to verify / qa-test / demo. |
| Slack message | explore, build, triage, review, answer, security-review, verify, qa-test, demo, all others via chat | Natural language in DMs or threads. The classifier extracts intent and repo from the message. Questions route to answer; "security review owner/repo" runs security-review. The two dependency workflows are GitHub-only — they need a PR number no Slack message carries, so such a request falls through to chat. Which triggers the bot will actually suggest in a thread is composed from the workflows that declare a chat: block and are enabled on that deployment, so the set is per-instance. |
| Cron schedule | issue-triage, pr-review, repo-health, security-review, dependabot-pr-merge, dependabot-ci-fix | Periodic scans. The triage cron is a fallback for deployments without webhooks. The review sweep (every 30 min) runs even with webhooks on: it is what reaches a PR whose fix chain ended without pushing a commit — attempts exhausted, an infrastructure failure, a flaky deferral — where no further check_suite event will ever fire, and it is the re-pickup that makes dropping (rather than queueing) the loser of the per-PR run lock safe. The security-review cron runs weekly (Monday 10:00), fanning out one run per managed repo. The two dependency-PR backstops run daily even with webhooks on — dependabot-pr-merge (14:00) discovers green (clean) dependency PRs and dependabot-ci-fix (15:00) discovers ones that can't merge on their own — settled-red checks, or a behind / dirty / blocked mergeable_state — each fanning out one bounded run per PR as a safety net for the real-time pr.checks_passed / pr.checks_failed paths. The sweeps only find candidates; whether a PR may be acted on (escalation, budgets, already-assessed, forks, a run already in flight) is decided at dispatch, in one place both they and the webhooks pass through. |
| CLI | All | npm run cli -- build owner/repo#N or npm run cli -- health owner/repo |
| Dashboard | All (via approval resume) | Approve/reject buttons on paused workflows in the admin dashboard. |
One label stops all of it. Apply the hold label —
lastlight-ignore by default, configurable as
hold.label — to any issue or pull request and no workflow in
this table runs on it: not triage, not review, not a fix, not a merge, and
no comment. It is checked before every routing rule and before every
dispatch, so the crons, the webhooks and a direct
@last-light … request all honour it — the direct request gets
exactly one reply naming the label, so it is refused rather than silently
ignored. Remove the label and the bot resumes, with nothing to clear.
Permission profiles
Each workflow runs in a Docker sandbox with a scoped GitHub token. The permission profile determines what the agent can do:
| Profile | Workflows | Permissions |
|---|---|---|
| repo-write | build, pr-fix, dependabot-ci-fix, dependabot-pr-merge | Full read/write — can push commits, create branches, open PRs (dependabot-ci-fix pushes fixes but never merges; dependabot-pr-merge enables auto-merge / merges and never pushes) |
| repo-write | security-feedback | Full read/write — updates SECURITY.md via PR and creates issues |
| review-write | pr-review | Can post review comments but not push code |
| issues-write | issue-triage, issue-comment, pr-comment, explore, answer, security-review, verify, qa-test, demo | Can read code, create/edit issues, post comments |
| read | repo-health | Read-only access to repo contents and metadata |