From ticket to production,
with the lights out.
Five steps. Numbers decide four of them. A person decides the last one, and silence never counts as a yes.
-
01 · TICKET
A ticket draws the perimeter.
A markdown file: the problem, Given/When/Then criteria and a
## Perimeterlisting the files the change may touch.Every path the agent changes must match it, checked before a pull request exists. One file outside, and no PR is ever opened.
core/perimeter.sh -
02 · SIX ROLES
Six roles, each in its own box.
One role writes code, in a throwaway sandbox with a token for that role only. The others confirm the ticket, read risky diffs, brief the human and watch production. Each has its own prompt and budget.
Your model, your coding CLI: Gemini, Claude or OpenAI, through
roles/ · core/crew.shgemini-cli,claude-codeorcodex, all behind the same guard. -
03 · SIX GATES
Six gates judge every pull request.
Risk, secrets, dependencies, static analysis, tests, mutation. Each prints one line,
GOorNO-GOwith the reason, and every threshold says where it comes from.A PR is judged by its base's policy, never its own: it cannot edit the gates that judge it. Mutation testing tells an honest test from one that passes and catches nothing.
gates/ · docs/adr/0033 -
04 · THE CANARY
A signed canary, judged by numbers.
One signed image per merge takes 5%, then 25% of real traffic. One PromQL file compares it with stable on errors and p95 latency.
A bad number rolls it back with nobody paged. No model decides whether a canary is healthy.
verify/slo.promql · core/launchpad.sh -
05 · ONE HUMAN
One human turns the lights back on.
The step to 100% waits for a person's GO, after a brief built from the release record. NO-GO rolls back.
Silence never promotes: with no answer, nothing moves.
core/promote.sh
Every way a change can go.
A clean release, a canary that rolls itself back, a fake test, a NO-GO the agent fixes, a risky diff, a freeze, a human who says no, a change out of bounds, a kill switch. Each is a path the code really takes.
Step through it yourself. The board plays every scenario station by station: what each step does, which file does it and the line it prints. These are scripted walks over the real code paths, with example versions and numbers. Run ./go board with the plane up and it shows your own release live.
Numbers, with where they come from.
./go demo from a clone to the canary's automatic rollback, after the first run, measured on a MacBook with podman. The first run downloads about 1.2 GB: expect 10–15 minutes.
lights-out doctor, init and gates from the published image on clean GitHub runners, amd64 and arm64, first pull included.
Claude Code's first live run, ticket 0001: 5 of 8 mutants killed on the changed lines. The mutation gate said NO-GO. Nothing merged.
docs/reference.md · live, on ClaudeThe demo needs no account, no API key and no cloud. Without a key no model is called: the agents replay answers recorded once.
README · what is recordedWhat is real, and what is recorded.
The gates, the signed build, the canary, the metrics, the rollback and the audit chain. Nothing in them is simulated.
Without a key the agents replay answers recorded once against the Gemini API; with a key and PROFILE=l1 they run live. The videos on this page are the board's scripted walks, with example versions and numbers.
Nothing is proven yet on a codebase that was not written for lights-out. The Google Cloud profiles are designed, not run.
Watch a canary roll itself back.
You need podman (or Docker on Linux) and git. Nothing else on your machine.
The demo
An agent turns a ticket into a pull request, six gates judge it, it merges, builds, signs and takes 5% of the traffic. Verify sees the errors and rolls it back on its own. The run ends with GO: demo.
$ git clone https://github.com/nicolaguglielmi/lights-out.git $ cd lights-out $ ./go demo
First run 10–15 minutes (it downloads about 1.2 GB and builds the toolchain), then about 7. Installing podman →
Gates on your repository
The lights-out wrapper runs init and the six gates from the published image, inside your repository, without cloning lights-out.
$ mkdir -p ~/.local/bin $ curl -fsSL -o ~/.local/bin/lights-out https://raw.githubusercontent.com/nicolaguglielmi/lights-out/v0.1.0/lights-out $ chmod +x ~/.local/bin/lights-out
$ cd ~/code/your-repo $ lights-out doctor # engine, image, key: one line each $ lights-out init # drafts .lights-out/ outside your repository $ cp -R ~/.local/state/lights-out/your-repo/.lights-out/. .lights-out/ $ git add .lights-out && git commit -m "add lights-out contract" $ lights-out gates # judges the last commit
Image ghcr.io/nicolaguglielmi/lights-out:0.1.0, amd64 and arm64, about 1.8 GB unpacked. Tags are mutable and the image is unsigned: pin it by digest.