dev-loop
Skill1 starsChanged 42 days agoMITView on GitHub
The dev loop of the AI Light Factory. Takes ready GitHub issues (from Matt Pocock's to-tickets, or any issues with acceptance criteria and blocking edges) and runs them unattended through build (Addy Osmani's agent-skills), cross-vendor review and fix rounds to a squashed draft PR, with at most two lanes, a severity ladder, a live board and hard budgets. It never edits files, never asks, never marks ready or merges. Also sets the factory up in a repo and runs the retro. Use when the user runs /alf:dev-loop.
8 other projects use a version of this.
Adopted by
Most recently taken up by
- ieuanign/skillstheir own version, first seen Nov 3, 2026
- paologum/claude-skillstheir own version, first seen Oct 25, 2026
- nodealchemy/powernode-platformtheir own version, first seen Oct 25, 2026
- ceasarXuu/AstartesSkillstheir own version, first seen Oct 21, 2026
- nubjs/nubtheir own version, first seen Oct 8, 2026
- dougmotshell/mathematics-studiestheir own version, first seen Aug 1, 2026
Other versions8 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 8
- nubjs/nub★ 4.4ksame name
- ceasarXuu/AstartesSkills★ 4same name
- nodealchemy/powernode-platform★ 4same name
- amjadjibon/skills★ 2same name
- ieuanign/skills★ 2same name
- XIYBHK/claude-skills★ 1same name
- dougmotshell/mathematics-studies★ 0same name
- paologum/claude-skills★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
The file
We split this skill into the rules it gives an agent
10 rules. 0 are common, 3 are distinctive, and 7 are very specific to this project.
0
Common
Most of the 6 edited versions we read keep these.
3
Distinctive
This author's own choices. Worth a look.
7
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 20
If .factory/config.json is missing, run setup first, whatever mode was asked for.
- Very specific: line 52
Config and companions: read .factory/config.json.
- Very specific: line 60
Readiness gate: run alf-ready <n> for each ticket.
- Distinctive: line 70
Never create a seat any other way, and never run git worktree add yourself.
- Very specific: line 75
Wait in the background: Run alf-wait $RUN as a background command, then end your turn.
- Very specific: line 92
Write the judged list to $RUN/<n>/judged-r<round>.md.
- Distinctive: line 110
Every decision the human could make was taken in grill-with-docs and written into the ticket by to-tickets.
- Distinctive: line 114
If a ticket's blocker has an open PR, base the branch on the blocker's branch.
- Very specific: line 126
The growing-coordinator trap: When your context gets large, or a parent is done, stop cleanly and continue with /alf:dev-loop --resume …
- Very specific: line 132
Remove factory:in-loop from drafted issues, then touch $RUN/ended to release the guard for this session.
“Common” is measured: kept by at least 6 in 10 of the 6 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
dev-loop: the dev loop
$ARGUMENTS picks the mode:
| Mode | Example | What it does |
| setup | /alf:dev-loop setup | Once per repo. Follow ${CLAUDE_SKILL_DIR}/references/setup.md. |
| run | /alf:dev-loop #40 --budget 2 | Runs a set of tickets through the dev loop (this page). |
| resume | /alf:dev-loop --resume 2026-10-01-invoice-export | Continues a run from its state.json. |
| retro | /alf:dev-loop retro | Works out which stage should have caught each problem in a run. Follow ${CLAUDE_SKILL_DIR}/references/retro.md. |
If .factory/config.json is missing, run setup first, whatever mode was asked for.
Where the dev loop sits in the line
The stages before and after are other people's skills, plus /alf:verify:
- Decisions:
/mattpocock-skills:grill-with-docssettles the open questions and writes ADRs todocs/adr/and terms toGLOSSARY.md. - Plan:
/agent-skills:planwritestasks/plan.md. - Tickets:
/mattpocock-skills:to-tickets tasks/plan.mdpublishes GitHub issues with acceptance criteria andBlocked byedges, labelledready-for-agent. - Dev loop: this skill.
- Verify:
/alf:verify, once per parent or milestone.
Run mode
Target: one of
#40, a parent issue: its sub-issues, plus open issues whose## Parentsection names#40#57, a single ticket with no childrenmilestone:NAME, every open issue in the milestonelabel:NAME, every open issue with the label (for examplelabel:ready-for-agent)
Flags:
--lanes 1|2: default from config (2). Three or more is refused.--budget N: stop starting new tickets once N have reached a draft PR.
Output: draft PRs, each squashed to one commit, assigned to the human, with a decision log in the body and bot threads already swept. Plus a run directory with the board and the ledger.
You are the Orchestrator. You own the frontier, the base branches, dispatch, the ladder, the board and the budget. You never edit a repository file, never read a full diff, never build, never review. You dispatch seats and read one-line results. Yours is the longest-lived and most expensive context in the run, so everything bulky goes to files and you read only the answer.
The plugin root is ${CLAUDE_PLUGIN_ROOT}. Scripts are in its bin/. Seat contracts are in ${CLAUDE_SKILL_DIR}/seats/. Seat prompts are in ${CLAUDE_SKILL_DIR}/references/prompts.md, and the state machine is in ${CLAUDE_SKILL_DIR}/references/state-machine.md. Read both before the first dispatch.
Start of run
- Config and companions: read
.factory/config.json. Every path undercompanionsmust exist (they point at agent-skills'incremental-implementation,test-driven-developmentandcode-review-and-qualitySKILL.md files). If one is missing, re-run the companion step of setup. A companion that does not resolve would be ignored silently by the worker. - Run directory:
RUN=.factory/runs/<YYYY-MM-DD>-<target-slug>(on resume, the existing one). Add this session's id to$RUN/orchestrator.session. Do it on every start, including a resume, because a fresh session has a new id:
The guard uses this to block merge, ready, approve and non-draft PR commands from this session untilecho "${CLAUDE_SESSION_ID}" >> "$RUN/orchestrator.session"$RUN/endedexists. - Sweep first, before dispatching anything:
alf-seat sweep. Clean-up belongs at the start, because an interrupted run never reaches its end. Put KEEP and LEGACY lines on the board as SKIP rows. Never delete them yourself. - Fetch the tickets once. Resolve the target to issue numbers (for a parent,
gh api repos/{owner}/{repo}/issues/<n>/sub_issuesplus a search for"## Parent" #<n>). Write each body to$RUN/tickets/<n>.md. Nobody re-fetches a ticket: a worker that fetches it again pulls the body and every comment into a second context for no new information. - Readiness gate: run
alf-ready <n>for each ticket. A ticket that fails becomes a PARK row ("needs decision:<reasons>"), with the reasons commented on the issue and the labelfactory:needs-decision. That is an upstream defect for grill-with-docs or to-tickets, not a question for you. - Edges: take
Blocked byfrom each body, plus GitHub's native dependencies (gh api repos/{owner}/{repo}/issues/<n>/dependencies/blocked_by). Write$RUN/state.jsonwithlanesandbudgetfrom your flags, and every ticket as READY (all blockers merged, or open as PRs it can stack on), WAIT, or PARK. On resume, read the existingstate.jsonand re-check every LANE row withalf-seat list. - Print the board:
alf-board $RUN.
Each pass
- Take signals. Each
alf-waitresult is one line,ALF <role> <STATUS> .... Updatestate.jsonusing the state machine. - Advance lanes. Take READY tickets in order. Choose the base (see Stacking), run
alf-seat acquire developer --run $RUN --ticket <n>, write the prompt from the template, thenalf-dispatch developer <Seat> $RUN <prompt-file> --ticket <n>. Set the row to LANE, and label the issuefactory:in-loop.- A lane is a ticket in flight. Its steps are sequential (build, review, fix, ship), so a lane holds one worker at a time.
acquireenforces the limits:NO_FREE_LANEwhen the lanes are full, andBUDGET_SPENTonce--budgettickets have a PR. Both mean "do not start another".NO_FREE_SEATmeans wait.- Never create a seat any other way, and never run
git worktree addyourself. - For a review:
alf-seat acquire reviewer --ref <head-sha>. For round 2, add--seat <the round-1 Reviewer seat>. Use one seat per ticket's review, not one per round.
- Sweep threads, once per pass rather than once per PR:
alf-threads pending <every open draft in this run>. If any have threads and a Guardian seat is free, batch up toloop.guardian_batchPRs into one Guardian dispatch. The Guardian is outside the lane count. - Budget. Once drafted tickets reach
--budget, start no new lanes and let in-flight lanes finish. - Board. Print
alf-board $RUNafter every dispatch and every signal. - Wait in the background. Run
alf-wait $RUNas a background command, then end your turn. Never sleep or poll in a turn: a turn spent waiting re-sends your whole context and decides nothing.
Repeat until no ticket is READY and nothing is running.
Judging findings: the ladder
The Reviewer uses agent-skills' severities. Read only the findings file, and judge each finding:
| Finding | Call |
| Critical | Fix |
| Important, fix under ~20 lines | Fix |
| Important, larger | Not addressed: listed in the PR body for the human |
| Suggestion | Dropped, with a one-line reason |
| Any finding without evidence | Dropped: "no evidence" |
| Reach findings (contract, shared path, deviation) | Never dropped. Fix if small, otherwise Not addressed, listed first |
Write the judged list to $RUN/<n>/judged-r<round>.md. Every call goes into the PR body, so what the dev loop decided without the human is auditable where they are already looking.
- Round 1 kept nothing: skip round 2 and ship.
- Round 1 kept something: dispatch a Developer fix, then a round-2 review of the delta only, in the same Reviewer seat.
- A Critical that survives round 2 parks the lane. Two review rounds, then a human.
A lane stops for five things only
- The Reviewer says the approach is wrong.
- A fix would touch a contract, a registry, or a migration the ticket did not ask for.
- A Critical survives round 2.
- A previously passing test fails for a reason outside the diff.
- The base branch moved underneath the lane.
None of these is a question. Each is a fact that makes the lane unable to proceed. A stopped lane becomes a PARK row and an issue comment with the evidence (label factory:in-loop swapped for factory:parked), and every other lane keeps running. A worker that reports STOPPED (a cap was hit), DIED, SILENT or NOT_RUN also parks its lane, with the log path. Never re-dispatch silently.
You never ask
Every decision the human could make was taken in grill-with-docs and written into the ticket by to-tickets. If you want to ask, that is a defect upstream: record it as a PARK row, write the question onto the issue, and carry on.
Stacking
- If a ticket's blocker has an open PR, base the branch on the blocker's branch. Inherit the chain if the blocker is itself stacked.
- For a parent or milestone target, stack on the tip: base each new ticket on the most recently opened PR in the set that is still open. A hard dependency with an open PR overrides this.
- For a single ticket, never base on "the last PR I opened".
- Four places must agree on the base: one commit over it, the branch point, the review diff, and the PR base.
Named traps
- The live-shell trap. A seat whose terminal exists is not a running agent. The board checks the process and log; trust
CHECKrows over your memory. - The bundling trap. One ticket and one role per dispatch.
- The re-fetch trap. Workers get the ticket file and the acceptance criteria inline, never "go read issue #57".
- The silent-skill trap.
alf-dispatchrefuses any prompt whose seat contract or companion SKILL.md is not an absolute path that exists. A worker with noALFstatus line is NOT_RUN, and a review without its canary line is "review not run". - The provenance trap. Copy the Reviewer's
vendor=… model=… same_vendor=…into the ship prompt, so a same-vendor review is never shown as cross-vendor. - The growing-coordinator trap. When your context gets large, or a parent is done, stop cleanly and continue with
/alf:dev-loop --resume <run-id>in a fresh session.
End of run
- Print the final board.
- For each ticket:
alf-ledger ticket $RUN <n> drafted|parked|skipped pr=… rounds=… fixed=… not_addressed=… dropped=… reason="…". - Remove
factory:in-loopfrom drafted issues, thentouch $RUN/endedto release the guard for this session. - Summarise briefly: drafts opened (with links), parked tickets and why, and the spend from the board footer. Say that nothing was marked ready or merged, and that the next step is
/alf:verify <target>.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

