Spec
Last Light — Specification
This is the implementation-grade reference for Last Light. /how-it-works explains the philosophy and shape; /docs shows operators how to run it. This spec is the contract — the schemas, invariants, and lifecycle steps a re-implementation must reproduce.
System architecture
Every block in the diagram below is a spec page — click to jump straight to its schemas and invariants. The order top-to-bottom matches the order of the component pages.
Component pages
Eleven component specs plus this overview. Each follows the same shape: purpose, inputs and outputs, schema, behaviour, invariants, current implementation, rebuild notes.
Harness
The supervisor process. Boot sequence, lifecycle, the wiring that connects integrations, the router, the workflow engine, the sandbox, and the chat path. Crash recovery and signal handling.
Configuration
Every env var the harness reads, the typed config schema, model and variant overrides, sandbox backend selection, approval-gate enablement, secrets layout, and the STATE_DIR tree.
Integrations
Every event source: GitHub App webhooks, Slack Bolt/Socket Mode, the CLI, the built-in cron scheduler, and admin-dashboard triggers. The connector contract, authentication, normalization, and reply path for each.
Event Model
The canonical EventEnvelope schema. The shape every integration normalizes its platform-specific payload into before the router sees it.
Router
How an EventEnvelope becomes a workflow dispatch. Deterministic matching for predictable events, a cheap LLM classifier for @-mention comments and chat messages, plus the reply-gate short-circuit for paused workflows.
Workflow Engine
The YAML grammar, the phase runner (linear and DAG), loop iterations, approval and reply gates, the template engine's data flow, taskId scoping, idempotency, and the resume protocol that survives process restarts.
Phases & Prompts
Phase types, the template engine (substitution, conditionals, helpers, phase-output access), the variable context every prompt sees, and the catalogue of prompt files under workflows/prompts/.
Skills
The SKILL.md format, skill staging into the agent workspace, the catalogue of skills referenced by workflows and chat, and the agent-context/ persona layer that becomes AGENTS.md at session start.
Sandbox
Where all agent work happens. The agentic-pi runtime, gondolin/docker/none backends, the SNI-peek egress firewall, the built-in GitHub and web-search tools, LLM provider routing, and per-run GitHub App token downscoping.
State
The SQLite tables for resume substrate and the per-session JSONL event log for agent transcripts. The split rule: what goes where, why, and how the dashboard reads both.
Chat
The pi-ai in-process chat runtime — the non-sandboxed path for low-latency conversational replies. Session resumption from messaging_messages, read-only GitHub tools, and the deliberate split with the sandboxed workflow path.
Last Light is a GitHub repository maintenance agent. External systems
(GitHub App webhooks, Slack, CLI, cron, the admin dashboard) emit events;
a normalization layer turns each into a canonical EventEnvelope; a
deterministic router decides which YAML workflow to run; the workflow
engine executes phases, each one calling out to an agent runtime
(agentic-pi for sandboxed phases, pi-ai in-process for chat). Every
phase writes to SQLite plus a per-session JSONL event log that the admin
dashboard reads.
The rest of this spec breaks the system down one layer at a time. Start on this page for the picture and the vocabulary; jump to the component pages for the contracts and schemas needed to reimplement each piece.
A note on the runtime. Sandboxed work runs through
agentic-pi(a Node library that supervises sandboxed agent sessions); the chat path runspi-aiin-process (a lighter, sandbox-free library suited for low-latency conversations). Both are independent of the LLM provider — Anthropic, OpenAI, and OpenRouter all work, selected per task via Configuration. An earlier version of Last Light used OpenCode; references to it in older code comments are historical.
Glossary
The terms used across this spec, in dependency order.
- EventEnvelope — the normalized event passed from connector to
router. Same shape regardless of source (GitHub webhook, Slack message,
CLI invocation, cron tick, dashboard trigger). Carries
type,source,sender,repo, optionalissueNumber/prNumber,body,title,labels,authorAssociation, the raw payload, and areply()callback the engine uses to post back. Full schema in Event Model. - Workflow — a YAML file under
workflows/*.yaml. Declares a sequence (or DAG) of phases the runner executes. The runner is workflow-agnostic: every behaviour (build, triage, review, explore, health) is just another YAML file. Full grammar in Workflow Engine. - Workflow Run — one execution of a Workflow against a triggering
event. Persisted in the
workflow_runstable withstatusofrunning,paused,complete, orfailed. Resumable across process restarts. - Phase — a single step in a Workflow. Either a
contextcheckpoint (no agent invocation), anagentphase (one agent session), or aloopphase (an agent phase that iterates on reviewer feedback, e.g.reviewer_2,reviewer_fix_1). - Execution — one agent session: a single phase running, or a single
chat turn. One row in the
executionstable with tokens, cost, stop reason, and a pointer to the JSONL event log for that session. - Skill — a directory under
skills/<name>/containing aSKILL.md(with mandatoryname+descriptionfrontmatter) plus optionalscripts//references//assets/. A phase declaresskills: [a, b, …](or sugarskill: <name>); the runner stages each into<workspace>/.agents/skills/<name>/and pi-coding-agent’s built-in auto-discovery surfaces them as an XML catalogue in the system prompt. The agent reads each SKILL.md on demand via itsreadtool — pi’s progressive-disclosure model.prompt:andskills:can coexist on the same phase. - Profile (
GitAccessProfile) — one ofread,issues-write,review-write,repo-write. Determines which scopes the GitHub App installation token receives for a given workflow’s sandbox. A triage run literally cannot push code. - Sandbox — the isolated environment one agent session runs in. Two
backends:
gondolin(QEMU micro-VM, default) anddockercontainer. Both apply a default-deny network egress policy and receive only a scoped GitHub token, never the App PEM. See Sandbox. - Session — the agent runtime’s per-execution conversation,
identified by an
agentSessionId. The same id appears in the SQLiteexecutionsrow and in the JSONL filename of the session’s event log, making the two views joinable. - Agent context (
AGENTS.md) — the persona + rules layer concatenated fromagent-context/*.md(soul.md,rules.md,security.md). Materialized into the sandbox at session start; also injected into the chat path’s system prompt. - Approval gate — a pause point declared on a phase. When hit, the
run persists with
status: pausedand a row inworkflow_approvals. The user resolves it via GitHub comment, Slack slash command, or the dashboard; the runner resumes from the next phase. - Reply gate — a softer pause used by the explore workflow’s Socratic loop. The agent posts a question, the run pauses, the next maintainer comment is fed back as the next loop iteration’s input.
Rebuild checklist
A re-implementation in any stack must provide all of the following. Each item links to the page where its full contract lives.
- An HTTP receiver for GitHub webhook payloads with HMAC signature verification — see Integrations §3.1
- A Slack socket-mode client (or alternative chat surface) for messaging — Integrations §3.2
- A CLI entrypoint that dispatches workflows for ad-hoc runs — Integrations §3.3
- A cron tick that fans out one dispatch per managed repository — Integrations §3.4
- An
EventEnvelopenormalizer per integration that produces the canonical shape — Event Model - A deterministic router with a single LLM call for build-intent
classification on
@-mention comments — Router - A YAML workflow loader with a typed schema validator — Workflow Engine §6.1
- A workflow runner with linear and DAG execution, loop iterations (max-count + until-condition), approval + reply gates, and resume across process restarts — Workflow Engine §6.2–§6.5
- A template engine for phase prompts with phase-output and scratch variable resolution — Phases & Prompts
- A skill staging mechanism — declared skills materialised at
<workspace>/.agents/skills/<name>/per phase so the agent runtime’s auto-discovery surfaces them via progressive disclosure — and an agent-context layer (AGENTS.mdinjection) — Skills - An isolated agent runtime per session with default-deny network egress and SSRF protection against cloud-metadata endpoints — Sandbox §9.1–§9.3
- GitHub App installation token minting with per-run profile downscoping. The App PEM never reaches the sandbox — Sandbox §9.4
- Provider-agnostic LLM routing inside the sandbox (Anthropic / OpenAI / OpenRouter) — Sandbox §9.5
- A persistent store with tables for executions, workflow runs, approvals, messaging sessions/messages, plus a per-session JSONL event log. The split between resume-state and event-log is load-bearing — State
- An in-process chat runtime distinct from the sandboxed path, for low-latency replies with read-only GitHub tools — Chat
- An admin dashboard that reads the persisted state and exposes resume controls
- Crash recovery: any
pausedorrunningworkflow rows must be picked up on harness boot — Harness