skill
openlanes
Concurrent Agentic Software Factory based on agentilda — https://github/kigster/agentilda. Instead of TUI it display css based ui. It's a self contained software factory that only requires Redis to wo
Set up
Skills are installed by placing their folder where your agent loads skills.
The download comes from github.com/kigster/openlanes itself, not from our server, so what you get is exactly what its author published.
Install instructions come from the README
README
synced Oct 26, 2026Open Lanes: AI Software Factory
[!IMPORTANT]
OpenLanes is one of few factories that can autonomously execute and implement features and merge them autonomously as well. This is configurable, and if turned on a dedicated agent provides risk assessment of whether or not this plan is safe to merge without human oversight.
OpenLanes® is an open source CLI software factory written in Ruby (but can build products in any language), that runs inside your project repo. It can help create specifications for new features (from CLI or the Web UI), and it manages the various states of development of this feature, passing it from one specialized agent to the next.
In a very brief and simplified way, creating a new feature creates it's .plans folder and the spec.md file, which is then populated by an initial agent based on the title, the spec.md is then opened in your system editor to allow you to define additional details, then a researcher agent adds some amount of research (based on both your existing codebase and external web searches), the writer then polishes the spec, before it is passeed to another agent that determines any interdependencies between plans and records it.
Next phase is the planning phase, performed by a single agent who creates plan.md, with backend and front-end features split as there is a dedicated agent for each.
After the planner is finished, builders pick it up and when they are done in their worktree, the harness (ruby dispatcher) submits a pull request and invokes several agents to perform PR review. There are three types of reviewers who all use different models, and one specializes on security. The reviewers are able to create and attach the PR labels used in this process. They can reject the PR (and almost always do), which returns it to the builders who address the feedback and re-submit the PR. The number of times this may occur is defined by "rounds" and in most cases the builders fix the PR to reviewer satisfaction.
A currently planned but not yet implemented feature is once the PR has review-approved label, another agent determines the risk surface area and depending on what it finds attaches one of two labels: auto-merge or human-should-merge etc.
.plans/001.00-<emoji>-plan-title/{spec.md,plan.md,....}
Additional files are created in the plan's folder, and when the plan is finished it's moved into .plans/archive folder.
The Tech Stack
The harness and the Engine is written in Ruby, and uses deterministic state machine to move plans from one state to the next.
The engine & backend use redis (defined by $REDIS_URL) for coordination and resource locking, and AASM gem for state machine transitions.
The web control room uses - React 19 and TypeScript 5.8, built with Vite 6, Tailwind CSS 4 and Radix UI, TanStack Query fetches data; @microsoft/fetch-event-source receives the server's event stream.
CodeMirror 6 edits Markdown. marked and DOMPurify render it, and highlight.js colors code blocks.
Quick Start
brew tap kigster/tap
brew install openlanes
# cd into your project directory, which can be nearly blank or can be an established codebase
ol setup # downloads dependencies, ensures your `gh` is authenticated or runs `gh auth login -w`
ol init # interactively creates the configuration file .openlanes.yml for project specific settings
ol start -j 8 # number of concurrent processes allowed
New project, or starting from an example app? docs/STARTING-AN-APP.md walks through every step, including .plans/APPLICATION.md.
This starts all the parts, and it's recommended to start this inside a screen or a tmux session that survives your terminal disconnecting.
Running OpenLanes via mprocs
If you want to see how OpenLanes runs and what it logs, you can drop this mprocs.yml file in the root of your repo, and use mprocs command to start OpenLanes. The Ruby Gem sources comes with one, but it's not distributed as part of the gem package. The gem souces are available on Guthub.
---
# `just start`: the engine, its API server and the page, all from this
# checkout. The page is Vite's dev server on 5173, not the built web/dist,
# so an edit to a .tsx or .css file is recompiled and pushed to the open
# page at once; Vite proxies /api to the API server on 11000.
#
# Everything lands in log/development.log: the engine and the API server
# through SemanticLogger, in colour here though the gem's default is plain,
# and bin/ol-tee adds Vite and what the API server prints itself. The
# engine is not piped, so its pane keeps its live board.
procs:
engine: ol start -j 4 --no-web --log-format color
api: bin/ol-tee api bundle exec exe/openlanes web --no-open --log-format color
# npm ci again whenever package-lock.json is newer than what is installed.
web: bin/ol-web
logs: tail -F log/development.log
You write a short feature brief; a team of specialist coding agents researches it, specifies it, plans it, builds it and reviews it, until it is a set of reviewed, CI-passing pull requests. You merge. Every feature is a folder under .plans/, and the folder's name is its state. The gem installs three names for the same executable: openlanes, lanes and ol.
Picture is worth a few words ;)
Above is the screenshot of the web component of open lanes. When you run ol start locally, this web UI automatcally opens and you can add your specifications via the UI, or you can execute ol create command on the CLI at the root of your project.
The software uses distributed locking using redis, so $REDIS_URL is the only thing you need to configure.
Each open control-room tab holds one Redis connection of its own, its subscription to board updates. On a shared or hosted Redis with a connection cap (maxclients, or a paid plan's limit), count one connection per open tab on top of the engine's own. A Redis user whose ACL forbids SUBSCRIBE still gets a working board: the page falls back to polling every 250 ms.
Once you boot the web UI, you may notice swim lanes (those are plans that existed already in the .plans folder, or you created via "Action -> New Idea" menu.)
One of the coolest parts of this, is if you have another laptop or a desktop, you can git clone your project there too, and start openlanes there as well. As long as $REDIS_URL is same as the first instance, and the github URL of the project matches, the two instances will cooperate and a single web UI will show remote nodes in a read-only mode.
Shortly we'll provide a video of this in action.
Quick Walkthrough the UI
It might help explain what various parts are. Starting top left and going clockwise:
Top Nav
- OpenLanes logo
- The project being worked on (minesweeper) and it's URL and the node/machine
- How long this particular UI has been running, and how many agents are working
- Next it shows you the total number of tokens up and down, and the estimated price in USD, but this is assuming you pay by token. If you are on a Claude subscription this does count towards it, but it doesn't represent the actual cost you are paying (it might represent the cost Anthropic is paying though)
- Next is the button: "All Machines" → if other nodes join your project, there user@hostname
- Action button that offers you to create a "New Idea" which it turns into a
.plans/<idea-folder>or to select from existing plans not already taken by remote nodes.
- Action button that offers you to create a "New Idea" which it turns into a
- Further is the dark/light switch.
- The button "Stop All Local" terminates local openlanes instance and the web. It's a "oh-shit" button. Finally, a small circled ? pops up a bit of help to provide the context for what you are seeing.
Secondary Nav
Where you see the "Board" (our agents) and "Logs", and where to the right of the screen you'd see remote machines connected and working on other plans represented by small buttons/pills.
Main Swim Lanes Area
The swim lanes is the core of the "what's going on" feature of OpenLanes. Each represents a single plan, and you can see that in the screenshot only a single plan is being worked on — 001, and it's purpose is to "Create a Web Based Minesweeper Game".
Depending on how you setup your project with lanes init command, the new and researhed stages may or may not be part of the flow. OpenLanes offers three levels of feature investigation:
- Full — this engages all of the agents, starts with the spec, performs research, rewrites it using copywriting skills, and when done, hands it off to the planning agent, planner. planner's job is to read the
spec.mdand writeplan.mdbreaking it into executable tasks by the backend engineer (backend-coder) and a frontend-engineer (frontend-coder). Once the plan is written, the control is passed to the two implementing engineers who remain in communications by means of a shared state JSON file. They can send messages to each other and divide and conquer the work. Once they are done, the report back to the dispatcher AI agent (the main thread that watches over everyone), which then creates a PR, and passes control over to the reviewer agent "staff-reviewer". Hans can reject the PR twice, after which another agent with the goal of breaking deadlocks and ties, named "arbitrator", makes a final call if this PR is good to go. - Plan – a simplified pipeline that assumes spec.md is already written by you. It invokes planner → backend-coder (+ frontend-coder) → staff-reviewer, for a spec you already trust.
- Quick — is a short mechanical tasks executed by a faster/simpler
chores-coder→staff-reviewer.
While the project select the default, each plan can override this on a case by case basis.
Right Drawer Area
This is where you can communicate with the agents if they get stuck. You can also pop out a copilot window that allows you to chat with the main dispatcher AI.
What it is
OpenLanes® is an open source software factory written in Ruby. Its agents run on Claude Code by default, through claude -p, which skips loading your own configuration, skills and plugins; codex and pi can stand in per phase.
For each feature it creates a folder under .plans, and a series of specialized AI agents turn it into a finished feature. The harness opens the pull request, staff-reviewer reviews it, and verifier runs the app through the plan's How to test before you merge.
The state diagram looks something like this:
---
config:
layout: elk
theme: forest
look: handDrawn
---
flowchart TB
B[You: brief in spec.md<br/>⚪ New] --> L[researcher<br/>🔎 research]
L --> Y[spec-writer<br/>📋 full spec]
Y --> P[planner<br/>⭐ plan.md]
P --> BE[backend-coder 🟡<br/>+ frontend-coder 🎨<br/>build]
Q[You: --starts building] --> R2[chores-coder<br/>🟡 one mechanical task]
BE & R2 -->|the harness pushes<br/>and opens the PR| H[staff-reviewer<br/>🟢 👀 review]
H -->|🔴 changes requested| BE & R2
H -->|approved| C[verifier<br/>🧪 runs the app through How to test]
C -->|🔴 a step failed| BE & R2
C -->|ready-to-merge, CI green| M[You: merge<br/>✅ Approved & Merged]
L & Y & P & BE -.->|a human must decide| X[⭕ 🛑 blocked.md<br/>unblocker folds answers back]
X -.->|by its contents| P
X -.->|blocked too long<br/>while others wait| BOMB[💣 the run halts]
RB[rebaser] -.->|resolves a rebase<br/>onto main that stopped| BE
Agents may write code, tests and plan documents, but never commit, push or merge. The harness checks that, then publishes the branch and opens the pull request itself.
Manual Install
Requirements:
| Need | Why |
|---|---|
| Ruby 3 or newer | the gem; this repository is developed on Ruby 4 |
claude CLI | the default agent adapter; codex and pi are optional alternatives per phase |
git | one worktree and branch per plan (--isolation shared needs no git) |
gh, signed in | pushing branches, opening pull requests, resync prs |
| Redis | the control room (start, web); run falls back to headless without it |
gem install openlanes agent-lock agent-evals -N
ol --help
ol skills install # copy the bundled skills into ~/.agents/skills (-n to preview)
echo 'eval "$(ol completion zsh)"' >> ~/.zshrc # or bash; add to your shell rc
echo 'eval "$(ol completion bash)"' >> ~/.bashrc # for bash; add to your shell rc
agent-lock (alock) lets several agents share one checkout without overwriting each other's files.
skills install copies rather than links, so re-run it with --overwrite after upgrading the gem.
Usage
Run these anywhere inside your project:
ol setup
ol init # .openlanes.yml and .plans/ at the project root; -y asks nothing
ol create tax rule dsl # .plans/000.00━━⚪━━❯tax-rule-dsl/spec.md, drafted and opened
# finish the brief in spec.md and save it
openlanes run 001 -n # dry run: which agent would take plan 001
openlanes start # engine plus control room on http://127.0.0.1:11000
openlanes status # what is up; `openlanes stop` stops it, `start --background` detaches it
openlanes plans # every open plan, its state and its pull requests; --all adds archived ones
openlanes start runs until you press Stop All Local on the page, Ctrl-C, or run openlanes stop; it restarts the engine or the page if either crashes. Answer the agents' questions under Needs You, create new plans, and watch the board. For scripts and CI, openlanes run 001,002 works just those plans, headless, and stops at the first round in which none of them changed state; --until planned stops them earlier.
init settles four project-wide decisions before any plan exists, in this order: CI (it names what the folder already runs, or offers to scaffold GitHub Actions, CircleCI or Jenkins), who merges a reviewed pull request (human or auto), the desired codebase quality (high or mvp), and the stack, detected from marker files such as Gemfile or package.json. With ANTHROPIC_API_KEY or OPENAI_API_KEY set it first asks you to describe the app, and skips the questions the description answered. The stack is also written to docs/decisions/stack.md.
openlanes init -y --ci github_actions --merge-by auto --quality mvp # ask nothing, no network
openlanes init --reconfigure # ask again with the current answers as defaults; shows the diff first
openlanes init --reconfigure -n # the diff only, nothing written
Under -y the defaults are --ci none (or what the folder already runs), --merge-by human, and no desired_codebase_quality (the first ok-not-perfect review asks it); a folder with two languages gets no stack:.
[!NOTE]
.openlanes.ymlfiles written by this release can holdci:andstack:. An older openlanes refuses those keys as unknown, so upgrade every machine that reads the file.
For a small mechanical task, skip research and planning:
openlanes create --starts building split big migration
Core concepts
Plan folders
Every feature is one folder under .plans/ (override with PLANS_DIR), named NNN.MM━━<emoji>━━❯<slug>:
.plans/000.00━━✅━━❯dev-foundation
.plans/001.00━━🟡━━❯tenancy-households
.plans/001.01━━⏪━━❯schedule-k1-backfill # shipped first, documented afterwards
.plans/002.00━━⚪━━❯tax-rule-dsl
NNN.MMis set once and never changes. Branches (<user>/NNN.MM-slug), pull request titles ([NNN.MM] ...) andpull-requests.mdall join on it.MMis00for a normal plan;01to99marks a retroactive plan (openlanes create --after 001).- The emoji is the state. A transition renames the folder (through
git mv), and a folder may only claim a state its files prove. - The name has no spaces, so Tab completion works and nothing needs quoting. Each state emoji is a single code point that terminals draw two cells wide; in iTerm, turn on Unicode version 9+ widths so the columns line up.
openlanes resync dirsrenames any folder whose name its contents contradict. It also migrates folders named before this convention (NNN.MM-<emoji> → <slug>, or with the older emoji ⚪️ ⭐️ ⭕️ 🅱️ ☢️ 🕰️), which are still read, and moves finished, deferred and discarded plans into their subfolders. Preview it with-n.
A plan that is no longer active moves into a subfolder, with the date it moved inserted before the slug:
.plans/archive/002.00━━✅━━❯[2026-10-13]━━dev-foundation # ✅ and 😎
.plans/deferred/018.00━━💤━━❯[2026-10-13]━━deploy-observability # 💤
.plans/abandoned/024.00━━❌━━❯[2026-10-13]━━external-resources # ❌
.plans/ itself then lists only the work still open. Plan numbers count every folder, so a number is never reused. Reviving a plan moves it back into .plans/ and drops the date.
While an agent works on a plan, its lock (process id, host and start time) sits in the runtime state directory, ${XDG_STATE_HOME:-~/.local/state}/openlanes/<project>-<hash>/locks/<ordinal>.lock, beside the run's openlanes-state.json. Nothing runtime is written under .plans/, and the folder name never changes for a lock. plans and the control room show the plan with 🔑, adding the host for a lock taken on another machine, which is never cleared automatically. resync dirs removes a lock whose process is gone on this host. A legacy locked-by.pid or .plans/openlanes-state.json from an older release is still read, for one release. See docs/decisions/decisions-on-runtime-state.md.
Each run also appends a progress journal beside it, ${XDG_STATE_HOME:-~/.local/state}/openlanes/<project>-<hash>/journal/<run id>.ndjson: one fsync'd, schema-versioned JSON line per engine event (run, assignment, agent pid, start time, session id, trace file and offset, transitions, extensions, questions), validated by schemas/journal-event.schema.json and mirrored to Redis on openlanes:<repo>:<node>:journal. The file, not Redis, is the record that survives a killed engine. Read the agents still in flight with jq -c 'select(.kind | startswith("agent_"))' <file>, or in Ruby with OpenLanes::Engine::EventJournal::Replay.call(path). See docs/decisions/decisions-on-progress-journal.md.
See docs/decisions/decisions-on-plan-folder-naming.md for the decisions behind it.
States
Generated by openlanes docs; openlanes states draws every transition.
| Symbol | Meaning | Files required |
|---|---|---|
| ⚪ | New | spec.md |
| 🔎 | Researched | spec.md |
| 📋 | Ready for Planning | spec.md, plan.md |
| ⭐ | Planned | spec.md, plan.md |
| 🟡 | Building | spec.md, plan.md |
| 🎨 | Building UI | spec.md, plan.md |
| 🟢 | Ready for Review | spec.md, plan.md, pull-requests.md |
| 👀 | In Review | spec.md, plan.md, pull-requests.md |
| 🧪 | Verifying | spec.md, plan.md, pull-requests.md |
| 🔴 | Changes Requested | spec.md, plan.md, pull-requests.md |
| ✅ | Approved & Merged | pull-requests.md |
| 😎 | Deployed | deployed.md |
| 😱 | Rolled Back | rollback.md |
| 🚫 | Scrapped by Review | rewrite.md |
| ⭕ | Technical Block | blocked.md |
| 🛑 | Product Block | blocked.md |
| 💣 | Bomb | blocked.md |
| 💤 | Deferred | delayed.md |
| ⏪ | Retroactive | none |
| ❌ | Discarded | discarded.md |
🧪 sits between review and merge: verifier starts the app the way .openlanes.yml says and walks the plan's How to test. 💣 is a ⭕ or 🛑 left too long while other plans depend on it; the run halts until someone answers.
Besides those, each plan folder holds state.json (each round's agent, model, tokens and time, the agents' signatures, their journals, and the messages between them) and, while building, implementation-plan.md, plan-backend.md and plan-frontend.md.
Agents
From openlanes agents list; read one with openlanes describe <name>.
| Agent | Handles | Advances to | Runs on | Lanes |
|---|---|---|---|---|
researcher | New | Researched | haiku/medium | full |
spec-writer | Researched, Retroactive | Ready for Planning | sonnet/medium | full |
planner | Ready for Planning | Planned | opus/high | full, plan |
backend-coder | Planned, Building, Changes Requested | Ready for Review | opus/high | full, plan |
frontend-coder | Planned, Building, Building UI, Changes Requested | Ready for Review | sonnet/medium | full, plan |
chores-coder | Planned, Building, Changes Requested | Ready for Review | haiku/medium | quick |
staff-reviewer | Ready for Review, In Review | Verifying, or Approved & Merged | sonnet/high | all |
verifier | Verifying | Approved & Merged | sonnet/medium | all |
unblocker | Technical Block, Product Block, Bomb | by contents | haiku/low | all |
rebaser | stopped rebases, from the harness | stages, never moves a plan | sonnet/high | all |
copilot | none: the control room's read-only copilot | read-only | haiku/low | all |
Agent definitions are Markdown files with frontmatter in agents/. A plan's spec.md frontmatter can change the adapter, model and effort per phase.
Lanes
| Lane | Route |
|---|---|
full (default) | researcher → spec-writer → planner → backend-coder (+ frontend-coder) → staff-reviewer → verifier |
plan | planner → backend-coder (+ frontend-coder) → staff-reviewer → verifier, for a spec you already trust |
quick | chores-coder → staff-reviewer → verifier, for one short mechanical task |
Blocks
When an agent hits a question that is not its to answer, it writes numbered questions (B1, B2, ...) into blocked.md and the plan stops at ⭕ (technical) or 🛑 (product). The loop never assigns a blocked plan. Answer on the control room page, or write answers into blocked.md and run openlanes unblock NNN.
The autonomy boundary
- Agents never run
git commit,git push,gh pr create,gh pr mergeand friends; web access is off unless an agent declaresnetwork: true. - After every agent, the harness verifies
HEADdid not move and no remote-tracking ref was moved by a push. - The harness, not the agent, commits, pushes and opens the pull request.
--no-git-pushturns that off. - Nothing merges automatically. verifier labels a pull request
ready-to-mergeonce the app passes its How to test and CI is green; merging and deploying stay manual.
Commands
Run openlanes <command> --help for flags and examples. Anything that writes to disk, GitHub or Linear previews with -n / --dry-run where it makes sense.
| Command | What it does |
|---|---|
init | write .openlanes.yml at the project root and create .plans/ |
start | the engine plus its control room, until Stop All Local or Ctrl-C |
web --demo | replay a sample run in the control room; otherwise start serves the page |
project (preview) | start, status, stop the app being built, from .openlanes.yml app: |
create (new, c) | create the next numbered plan folder and its spec.md |
run | run the agents over the plans named, headless, to a fixed point; -n to preview, --until to stop early |
plans | open plans, their states and pull requests (--all: archived too) |
index (idx) | write .plans/INDEX.md |
unblock | fold answered questions from blocked.md back into the plan |
worktree | create and seed a worktree for one plan |
resync dirs / resync prs | fix folder names; add [NNN.MM] prefixes to pull request titles |
agents list / agents describe, describe | the agent roster; one agent's definition |
mail send / mail read | messages between agents on one plan |
state sign / state show | an agent's signature in state.json; print it |
eval | score agents against evals/cases; --live runs them for real |
docs | generate the conventions document from the state machine |
states (diagram) | draw every state and transition |
uml / uml self | Mermaid class diagrams of the project's Ruby source, or of OpenLanes |
linear import / linear projects | export plans to Linear, one way |
skills install | copy the bundled skills into ~/.agents/skills |
completion | print a bash or zsh completion script |
version | print the version |
Global flag: -C, --no-color.
Configuration
.openlanes.ymlat the project root:project,lane,frontend,jobs,redis_url,redis_outage_hours,desired_codebase_quality,merge,ci,stack, and per-command defaults undercommands:. A typed flag beatsspec.mdfrontmatter, which beats.openlanes.yml, which beats the built-in default. Unknown keys are an error.spec.mdfrontmatter:lane,frontend,depth, per-phaseadapter/model/effort,implementation_suggestions,implementation_requirements,task-completed-when,how-to-verify.PLANS_DIR: the plans directory,.plansby default.- A lost Redis does not stop the run. After 10 seconds without an answer every agent is paused through its control file (its clock stands still), nothing new starts, and the outage is recorded in
.plans/openlanes-state.json. Redis is asked again after 1, 1, 2, 3, 5, 8 … seconds, never more than 30 seconds apart, each gap drawn at random below that (full jitter) so engines sharing one Redis do not ask in step;redis_backoff:in.openlanes.ymlchanges the three (initial: 1,cap: 30,jitter: true). When it answers, the agents resume. If it has not answered withinredis_outage_hours(1 by default, any positive number), the run shuts down the wayqdoes. On resume the log says how long Redis was gone and which copy of the run state won, e.g.Redis is back after 72s (by heartbeat), run state from file; resuming: the length comes from this node's last heartbeat in Redis, or from the engine's own clock when Redis came back without one, and the run state from whichever of Redis and.plans/openlanes-state.jsonhas the higherseq. The board page shows the same asrun.outage, and warns while the engine has not published for more than 15 seconds. A Redis that restarts empty (no AOF, an old RDB snapshot) does not stop the run either: the engine makes its commands group again (issue #59). events:in.openlanes.yml:enabledanddirfor the opt-in durable run log, read back withopenlanes events report. Seedocs/events.md.
Further reading
docs/STARTING-AN-APP.md: from an empty directory or an example tool start;docs/application-template.mdis theAPPLICATION.mdtemplate.docs/USAGE.md: settings, spec frontmatter, running and limits, handoffs, safety rails, GitHub and Linear, the factory integration test.docs/WORKFLOW.md: states, transitions, numbering and plan files, generated byopenlanes docs.docs/architecture/: class diagrams oflib/, generated byopenlanes uml self;before/is the frozen picture from before the 4.0 refactor.docs/decisions/: design decisions, including ones not built yet.docs/software-factory-proposal.md: a proposed next architecture.
Related projects: agentilda-ai-setup installs coding agents, skills and plugins from one configuration.yml, and ships the /plan-* slash commands that wrap this CLI; agent-lock coordinates agents writing in one checkout.
Development
bin/setup # or: just install
just test # rspec
just lint # rubocop, kept at zero offenses
just ci # lint, then tests with coverage
just format # rubocop -a, then mdformat
just update-workflow # regenerate docs/WORKFLOW.md
just update-architecture # regenerate docs/architecture/
just web-build # build web/dist, which the gem ships
just web-test # the page: eslint, typecheck, vitest with coverage
just web-e2e # Cypress against `openlanes web --demo`
OPENLANES_FACTORY_E2E=1 just factory-e2e runs real agents end to end and spends tokens; see docs/USAGE.md. The executables in exe/ find their own Gemfile, so they behave the same from PATH or another project's root. Code layout and conventions are in CLAUDE.md.
© 2026 Konstantin Gredeskoul