The 764-Line Skill That Runs Our Software Delivery
One Claude Code command, three roles, one exit to humans: what 1,213 agent runs taught us to write down
Between May and October our agents ran 764 tasks. The file that tells them how to do it is 764 lines long.
The match is an accident. The length isn’t. Almost every section of that file exists because the agent did something wrong once, and we decided it would never happen again.
This is deep dive #9 in my series on the ten Claude skills that earned a place in my agency. It’s the only one nobody else can install, because it’s ours. What you can copy is its shape, and at the end there is a skeleton you can drop into your own repo today.
What it does
My agency builds software for clients. This year our IT team went from 20 people to 9: three developers, three testers, two managers and one business analyst. Most of the work the other eleven used to do now goes through one agent.
The setup is simple. We run our own task board. A small Node.js runner watches it, and when a task is assigned to the agent’s account, it starts Claude Code with one command file and the task code. That’s all the runner does. Everything about how the work is done lives in the file.
The file sits in .claude/commands/, and since Claude Code merged custom commands into skills, it behaves exactly like a skill: a markdown file of instructions with $ARGUMENTS for the task code. It starts like this:
Run the full cycle of task $ARGUMENTS, from preparation to testing, in one process.
You are one agent playing three roles in order: PM (preparation), Dev (development), QA (testing).
One bot account for all roles -> start every comment with a role badge: **PM**, **Dev** or **QA**.
Maximum QA -> Fix cycles: 3From May to the end of October this file drove 1,213 runs on 764 tasks.
The three phases
PM. The agent reads the whole task tree, subtasks included. If the task has no testable requirements, it writes them into the task as acceptance criteria. If it does, it skips straight to Dev.
Dev. It creates a branch named after the task in every repo the task touches. It reads all comments first, and a bug report from QA is priority number one. It ticks off acceptance criteria as it goes, writes tests, pushes the branch and writes a report.
QA. Every acceptance criterion becomes a test case. The agent opens the app in a real browser, walks each scenario, checks that the screen shows real content and not translation keys or placeholders, runs the backend tests and attaches three to five screenshots to the task. If something fails, the task goes back to Dev. After three rounds it stops trying.
Then the task lands in “To test”, where our testers and team lead take over. The agent never marks anything as done for a client.
Where the 764 lines went
The three phases take about 470 lines. What surprised me when I counted is the rest: the orange bars are about people and failure, not about code. Here is what’s in them and why.
“Requirements” is not a header name
The agent decides which phase to start from by looking for testable requirements. Early versions searched for the literal string Acceptance Criteria. Then a task arrived with nine precise numbered requirements under the header "Requirements", in Russian. The agent didn't find its magic string, went to the PM phase and invented acceptance criteria for something that was already fully specified.
Now the file says:
"Testable requirements" means the meaning, not the header name. Any of these counts:
an Acceptance Criteria section, Requirements, or just a numbered list of conditions that
makes it clear what "done" means. Any language.The file keeps the story of the failure next to the rule. I recommend that. A rule without its reason gets “simplified” by the next person who edits the file.
Blocked, not To Do
When the agent hits a problem, it has to move the task somewhere. The obvious choice is back to To Do. That’s a trap: To Do is exactly the column the runner watches, so the agent picks the same task up again and loops.
Why Blocked and not To Do: To Do falls under the runner's trigger again and the agent loops.
Blocked is a terminal column for a human to sort out.The handoff: don’t fake it
Some tasks can’t be finished by an agent. It has no access to a third-party dashboard, the requirement needs a product decision, or someone has to set up DNS by hand. A model under pressure to finish will happily simulate a result. So this section is blunt:
If the task fundamentally cannot be done by you autonomously, do NOT simulate or invent a result.
1. Do your part in code, if there is one, and push it.
2. Leave a handoff comment: Why / Human checklist / Done in code.
3. Ask open questions with ask_task_question.
4. Move to Blocked, log your time. STOP.
Block only on real blockers (no access / a decision is needed / manual external work), not on "hard".
Hard things, you do.The last line matters as much as the rest. Without it, an agent learns that “blocked” is an easy way out.
Two readers
The longest non-phase section is about report format: 66 lines. Every report the agent writes has two very different readers. The client and the manager need to understand the result in a minute. The developer and the tester need files and lines. When we let the agent write freely, the second reader won every time, and the client drowned.
Get Anton Lytvynov’s stories in your inbox
Join Medium for free to get updates from this writer.
So every report starts with a short panel in plain words: what this is about, what was wrong, what was done. Only then comes the technical part.
A report without the plain-words panel is unfinished. Before changing status, reread your comment:
if it starts with "### Business Logic" or "### QA Test Report", you forgot the panel.This is the section I’d steal first. Agents write for engineers by default. Your clients aren’t engineers.
What it changed for us
Before this file, each step lived in a separate command: one for preparing a task, one for development, one for QA. A person had to move the task and start the next step, and tasks waited for that person more than they waited for the work. Putting all three roles into one run, with clear exits, means a task can go from the backlog to “To test” without anyone touching it in between.
It also moved the hard human work. Testing became the main human job in the company, and our testers now start from the agent’s report and screenshots instead of from zero. The developers spend their time on what the agent hands back: the Blocked column, architecture, and the tasks we decide not to give it.
Top 3 real uses for a small team
1. The overnight backlog. Small fixes and well-specified features go to the agent in the evening. In the morning a tester walks the To test column with the agent’s screenshots open next to the app. Nobody waits for a developer to be free for a one-hour fix.
2. Requirements that can be tested before anyone codes. The PM phase is useful even on its own. A vague task comes back with numbered criteria, and the client confirms them before any development time is spent. Arguments about “that’s not what I meant” happen here, where they are cheap, instead of after the code is written.
3. Reports a client actually reads. Every task ends with a plain-language summary at the top. Our managers can forward it to a client as it is, instead of rewriting developer notes.
Prompts to copy
These work with the skeleton below (or any similar skill). Replace 142 with your ticket number.
/task-cycle 142Run ticket 142 end to end. If it has no testable requirements, write them, then stop and wait for my OK before coding.Ticket 142 is in review. Do only the QA part: every acceptance criterion is a test case, walk them in the browser, attach screenshots, report in plain words first.You're stuck on 142. Don't guess. Write the handoff: why it's blocked, a checklist for a human, what is already done in code. Then move it to Blocked.How to install
You don’t need our board. The skeleton works with GitHub Issues, Linear or Jira as long as Claude Code can read and update tickets (through the gh CLI or the tracker's MCP server).
mkdir -p .claude/skills/task-cycle
$EDITOR .claude/skills/task-cycle/SKILL.md # paste the skeleton below---
name: task-cycle
description: Take one ticket from "ready" to "in review" in one run - prepare, implement, self-test, report. Use when asked to "run ticket X" or "do task X end to end".
disable-model-invocation: true
---
Ticket: $ARGUMENTS
You play three roles in order. Prefix every comment with **PM**, **Dev** or **QA**.
## Pick the phase
- No testable requirements (any header, any language, or a numbered list) -> PM
- Requirements present -> Dev
- In review -> QA
## PM
Write testable acceptance criteria into the ticket. Nothing else.
## Dev
Branch `feature/<ticket>-<slug>` from the base branch. Read all comments first; a QA bug report is priority 1.
Never push to main, master, develop or release.
## QA
Every acceptance criterion is a test case. Open the app and walk each one. Attach 3-5 screenshots.
Bug found -> back to Dev. Max 3 rounds, then Blocked.
## Report (every phase)
Start with 3-5 plain sentences for a non-technical reader. Then the technical part.
## Needs a human
No access, a product decision, or manual setup -> do your code part, write Why / Human checklist,
move to Blocked (never back to the column that triggers you), STOP.
Block on real blockers, not on "hard".Then open Claude Code in the repo and type /task-cycle 142. disable-model-invocation: true means it only runs when you call it, never because Claude guessed you wanted it. When you trust it, a script can start the same command per ticket with claude -p "/task-cycle 142". That's all our runner is.
Where it fails
It’s long. 764 lines is a lot of context on every run. If I started today I’d move each phase into its own reference file and keep the main file as a router. That’s exactly what progressive disclosure is for.
Rules pile up. Every incident adds a paragraph. Nobody deletes old ones. Twice a year somebody has to read the whole file and remove what no longer applies.
A browser test is not a human test. The QA phase catches broken screens and missed criteria. It doesn’t catch “the client meant something else”. That’s why the last column belongs to people.
It’s only as good as the ticket. The PM phase can make a vague ticket testable. It can’t make a wrong ticket right. Garbage in still means a well-tested garbage out.
Start with the skeleton. Every time the agent surprises you, add one rule and write down why. In six months you’ll have your own 764 lines.
Next: the 53-line skill that tells me when the agents need me.










