Building an Automated Development Harness with Claude Code

How I wired Claude Code into my entire development loop — from a ticket to a verified release — using small triggers, focused skills, and feedback loops that make the system improve itself.
A ticket lands on my board, and an agent takes it from there: plans the work, writes the code to my team's standards, opens a pull request, and — once I've merged — verifies it against the real deployed app, twice, in two environments. It interrupts me only when it hits a decision I'm the one who has to make.
Here's the whole thing on one diagram. 🤖 = an agent acts, 👤 = I act.
This post is the architecture underneath: the triggers, the skills, and the loops that tie them together. I'll name the skills and show how they fit — the prompt inside each one is tuned to my codebase, but the shape is what transfers, and you could build your own this week.
The one idea
The instinct is to write one giant prompt that plans, codes, tests and ships in a single conversation. It falls over fast — the context gets muddy and you can't tell which step failed. So I stopped thinking about "the task" and started thinking about the board:
The unit of automation isn't the task. It's the state transition on your issue tracker.
Every time a ticket changes column, one well-defined thing happens, and nothing else. Each transition is a clean boundary you can build, test and debug on its own. The tracker is already the source of truth for "what state is this in," so you don't invent an orchestration layer — you react to one.
Triggers: dumb on purpose
Two kinds of triggers, split by a simple question: is the thing I'm waiting for an event or a condition?
"Prepare a plan" and "implement" are events, so they arrive as webhooks. The receiver does almost nothing — authenticate, spawn one session, return:
1// The trigger is dumb: authenticate, spawn one session, return immediately.2app.post("/jira/implement", (req, res) => {3 if (req.get("x-secret") !== process.env.SECRET) return res.sendStatus(401);4 spawnTerminalSession(`claude "/implement-ticket ${req.body.ticket}"`);5 res.sendStatus(200); // fire-and-forget — the session runs on its own6});
Verification can't be a webhook, because "has this actually deployed yet?" is a condition, not an event. So a cron job polls every 15 minutes and only fires once the deploy has landed:
1# Pick up tickets whose deploy has actually shipped.2for ticket in $(tracker query 'assignee = me AND status = "In Testing"'); do3 deployed_to_dev "$ticket" || continue # the condition, not an event4 label "$ticket" verifying # guard against double-firing5 spawn "claude '/verify-ticket $ticket --env=dev'"6done
The rule the whole thing rests on: keep the trigger dumb and the skill smart. All the judgment lives in the skill, so I can rewrite a skill without touching the plumbing.
Which skill runs when
Every trigger runs exactly one named skill. There are only three — and the verification one runs twice, against two environments:
/scaffold-ticketrewrites the ticket and posts a plan for me to approve./implement-ticketruns the build pipeline and spawns/simplifyandpre-pras subagents./verify-ticketdrives the deployed app and posts a verdict — once in dev, once in staging.
The background routines further down are skills too; they just run on a schedule instead of a trigger.
Inside a skill
A skill is a focused, repeatable workflow. /implement-ticket is the workhorse, and its best trick is delegating to subagents — independent passes that catch far more than one agent grading its own homework.
The output is a draft PR that already follows the standards, has green checks, and has been through a cleanup pass. My job becomes review, not write. A few principles do the heavy lifting:
- Plan before code, and make a human approve the plan. A thirty-second read of a plan is the cheapest insurance in the system.
/scaffold-ticketposts a plan as a comment and stops — it writes no code and moves no ticket. - Standards are loadable context, not prompt text. Conventions auto-load by file path, so the agent writes code that looks like mine without being told every time.
- Verify against reality.
/verify-ticketdrives the real deployed app and bakes the environment into every screenshot. A green unit test isn't proof; watching the feature work is. - Never auto-do the irreversible. For one-way actions it drives up to the submit button, screenshots the ready state, and stops.
- Only notify when a human is the bottleneck. A system that pings you constantly is one you'll turn off.
The loops that make it compound
Wiring an agent to your tracker is the flashy part. The loops are what make it get better while I'm not looking. Three background routines learn from what already happened — and crucially, they propose; I ratify.
- The PR-review learner mines comments on merged PRs and promotes a lesson only when it recurs across multiple PRs and multiple reviewers. One person's preference doesn't become law; a thing three people flag does.
- The skill proposer reads back over my own sessions for repeated friction — the same file read six times, the same correction twice — and pitches a new skill. It never builds it.
- The docs auditor (
/context-audit) checks the standards files against the actual code and flags drift, because context files rot the moment the code moves.
Memory is just plain files — an index plus one tagged file per lesson, auto-loaded by path. No vector database required:
1---2name: prefer-whole-object-assertions3trigger: "**/*.test.ts" # auto-loads when touching tests4---56Assert on the whole object in one check, not field by field.7(seen across 3 PRs, 2 reviewers)
Nothing self-modifies in the dark. The day an automated system silently rewrites its own rules is the day you stop trusting it.
Build your own
You don't start with the whole board. You grow into it:
- Write one skill by hand and run it manually until it's good. When it slips, fix the instructions, not the output.
- Move your standards into loadable context so the skill stops needing to be told how your code looks.
- Add a pre-PR gate — tests, lint, types — as a separate pass it must clear.
- Wire exactly one trigger — a webhook on one status change — and resist adding more until it's solid.
- Add verification against a real environment, with evidence on the ticket.
- Add memory and one learning routine. Start with the PR-review learner; it pays for itself fastest.
Each step is useful on its own. You benefit at step one.
What it's great at, and what still needs me
Handles well: turning a reviewed plan into a standards-compliant draft PR, the grind of cleanup and quality gates and CI, and capturing real verification evidence so QA isn't taking my word for it.
Still needs me: ambiguous product calls, auth flows with 2FA, taste — where "correct" and "good" diverge — and anything irreversible, which it's built not to touch.
Which is the whole point. I'm not removing myself from the loop; I'm moving myself to the expensive parts of it and letting the harness carry the rest.
Built with Claude Code, a stack of small scripts, and an issue tracker. If you build your own version, I'd like to hear what you wired up.
Related Posts
Building Your First AI Agent with LangChain and Python
Learn to create intelligent AI agents that can reason, plan, and execute tasks autonomously using LangChain, OpenAI, and Python. Complete with code examples and deployment guide.
AI Morning Briefing — May 11th, 2026
Anthropic tops OpenAI in ARR at $30B, SpaceX deal doubles Claude Code limits, and Chinese AI labs carpet-bomb the frontier.
Building a Native SQL Client from Scratch with Claude Code
How I built a fully-featured PostgreSQL client in 48 hours using Electron, Svelte 5, and Claude Code — with inline editing, an AI assistant, and security hardening for open source release.