My day-to-day dev loop is built around ccmagic , my Claude Code plugin of dev workflow skills. The core of it is an interactive ticket lifecycle: Claude does the work, and I stay in the loop at each stage to answer questions and approve decisions. /ccmagic:work-ticket ENG-123 picks up a Linear (or JIRA) ticket, classifies it, branches, implements, self-reviews, and opens a PR. /ccmagic:review-ticket reviews the change against the ticket's actual scope and acceptance criteria. /ccmagic:pr-feedback triages review comments on the PR (from humans or agents), and /ccmagic:finish-ticket sanity-checks and merges.

Recently I added the piece that removes me from that loop entirely: /ccmagic:auto-ticket. Give it a ticket ID and it drives the whole lifecycle unattended, including the merge. This post covers what it does, how it's built to fail safely, and the part that matters most: when you should actually use something like this, and when you really shouldn't.

What it does

auto-ticket is an orchestrator. It chains the existing lifecycle skills in autonomous mode:

auto-ticket ENG-123
  → work-ticket      implement, self-review, open the PR
  → review-ticket    ticket-grounded review; fix CRITICAL findings, re-review (looped)
  → pr-feedback      apply fixes, reply to threads, file follow-ups, push,
                     validate locally, wait for CI + bot reviews (looped)
  → finish-ticket    merge gate: mergeable + CI green + no unaddressed change requests
  → summary          posted to the PR and the ticket

Every run ends in exactly one of two states:

  • Merged. The PR merged, the ticket moved to Done, and a run summary was posted to both the PR and the ticket.
  • Parked. Nothing merged. The ticket moved to a configured needs-human state (or got a needs-human label), with a comment explaining exactly what decision it's waiting on.

There is deliberately no third outcome. It never stalls waiting for input that isn't coming, and it never merges on a guess. When the work is genuinely uncertain, parking with a clear note is the designed behavior, and I treat a park as a success: the system correctly recognized that a human was needed.

To be clear about intent: this is built for solo-dev projects where auto-merge with no human in the loop is the point. If that sentence made you flinch, hold that thought until the "when not to use it" section, because your flinch may well be correct.

How it's built

Some of this was in my initial design, but most of the hardening came from a feedback loop I now use on everything: run the workflow for real, then hand Claude the full session transcripts and have it hunt for bugs, gaps, and inefficiencies in its own runs. Transcript review is what surfaced the pattern-fixing and delta-review problems described below, along with a handful of smaller issues like duplicate summary posts on retried steps. If you're building autonomous workflows, the transcripts are your telemetry. Read them, or better, make the agent read them.

One contract, six skills

The orchestrator and every sub-skill it calls share a small written contract. Each invocation gets a "grounding block" prepended to its arguments: the ticket ID, tracker, PR, run ID, and loop bounds. Each sub-skill ends its output with a machine-readable status handshake:

status: clean | fixable-findings | needs-human | done
reason: <one line, when not clean/done>
follow_ups: [anything filed or deferred]

clean means the step ran and found nothing left to fix (a review pass with no CRITICAL findings, feedback fully addressed). done means the step completed a terminal action, like the merge itself. fixable-findings sends the orchestrator into the relevant fix loop, and needs-human routes to a park.

The orchestrator parses the last handshake and decides the next step. A sub-skill that crashes or emits no handshake is treated as needs-human. That single rule eliminates a whole category of silent failure: an ambiguous result can't quietly become a merge.

The Sacred Rule

One rule sits above everything else, written into the skill in exactly these terms: if the ticket can't be found, stop. Never infer, guess, or fabricate what the ticket might contain. The ticket system is the source of truth.

This rule exists because of observed behavior, not paranoia. In early runs, when a ticket lookup failed, Claude happily invented a plausible-sounding scope from the ticket ID and the repo's context and started implementing it. An autonomous system that fabricates its own requirements and then merges against them is worse than no automation at all, so a failed lookup is now a hard stop before anything else happens.

Per-step subagents on per-step models

Each lifecycle step runs in its own forked subagent on a best-fit model: Opus for implementation and review, where judgment matters; Sonnet for PR feedback, validation, and the finish step; Haiku for the mechanical commit-and-push. This keeps the orchestrator's context lean across a long unattended run and puts the expensive model time where it actually pays off. Every step's model is overridable per repo.

This routing is also the cost story. I run on a Claude Max subscription, so I don't see a per-run dollar figure, but the shape of the spend is deliberate: a full run is one Opus-class implementation pass, up to a few Opus review passes, and cheap models for everything mechanical. On API pricing the same structure applies. If cost matters to you, the per-repo model overrides are the knob, and the bounded loops below are the ceiling.

Bounded everything

Every loop has a cap, and every cap has a defined exit. The review-fix loop runs at most 3 passes. The PR-feedback loop runs at most 3 passes. Local validation gets 2 fix attempts. The CI wait gets a configurable timeout (30 minutes by default). Hitting any cap means route-and-stop: park the ticket, post the reason, exit cleanly.

Reviews that fix the class, and reviews that diff

Two more field-run lessons made it into the review loop. First, when a review finding is one instance of a repeatable pattern (say, versioned asset references that a cache-bust missed), the fix loop is required to fix the entire class: apply the fix to every enumerated instance, re-run the search that found them, and only then re-review. One early run merged safely, but only just, because it point-fixed instances of a defect instead of reasoning about the defect class.

Second, re-reviews produce delta reports. A fresh review subagent has no memory of the previous pass, so the orchestrator hands it the list of findings it just fixed. The re-review verifies each one as fixed or not-fixed in a line each, and reports only net-new findings in full. Without this, pass 2 re-litigates pass 1 and the loop burns its budget rediscovering things.

The audit trail

Every run posts a summary to the PR and the ticket: what it classified the ticket as, what decisions it made without asking, review passes and findings, feedback applied and declined, follow-up tickets filed, and the final outcome. Unattended automation you can't audit afterward is automation you'll stop trusting the first time something looks odd. The summary carries a run ID, and posting is idempotent, so a re-executed step never double-posts.

When you should use it

The honest framing: auto-ticket pays off in direct proportion to the quality of your tickets and your safety nets. It's the right tool when most of these are true:

  • You'd merge it yourself without much thought. Well-scoped bug fixes, small features, dependency-adjacent chores, test additions, refactors with existing coverage. The kind of ticket where your own review would be a skim.
  • The ticket has real acceptance criteria. The review step grades the work against the ticket's stated scope. A ticket that says "fix the thing" gives it nothing to grade against, and ambiguity becomes a park (at best) or a plausible-but-wrong implementation that your CI then has to catch.
  • CI is trustworthy. The merge gate is "mergeable, CI green, no unaddressed change requests." If your CI is green when the code is broken, you've delegated the merge decision to a rubber stamp. Test coverage is doing the reviewing now; fund it accordingly.
  • You're solo, or the repo's norms allow bot merges. The whole design assumes no human review is required before merge.
  • Volume is the problem. The backlog of small, clear, low-risk tickets that never get picked up because each one is thirty minutes of context switching. This is where an autonomous driver earns its keep, especially running headless (more on that in the follow-up post).

When you shouldn't

  • No CI, or decorative CI. This is the hard disqualifier. Every other safety property in the system funnels through "CI is green" at the merge gate. Without meaningful automated tests, auto-ticket is an expensive way to merge unreviewed code.
  • High blast radius. Auth, billing, data migrations, anything security-sensitive, anything irreversible. The system is explicitly designed to park when uncertain, but the failure mode you care about here is confident-and-wrong, and no handshake protocol catches that. These tickets deserve a human even when they look simple. Especially when they look simple.
  • Untrusted people can write to your ticket tracker or your PRs. Once you go autonomous, the ticket body and the review comments are instructions that an agent with merge rights will act on. That makes them part of your trust boundary, and prompt injection is the attack. On a solo private repo with a private tracker, the only person who can inject is you. On a public repo where anyone can file issues or comment on PRs, an unattended agent that implements ticket content and applies reviewer feedback is a standing invitation. If outsiders can write to the inputs, keep a human on the merge.
  • Product decisions in disguise. "Improve the onboarding flow" is a design conversation wearing a ticket costume. Autonomous mode will either park it or make product decisions you didn't delegate.
  • Team repos where review is the point. If PR review is how your team shares context and catches design drift, automating the author *and* skipping the human reviewer removes both sides of that conversation. (Using work-ticket interactively and keeping a human reviewer is a fine middle ground.)
  • A codebase Claude doesn't know yet. ccmagic's review and implementation skills lean on documented conventions (context/conventions.md, architecture knowledge from /ccmagic:map-codebase). On a brownfield repo with none of that written down, do the interactive loop for a while first. You're training the harness, and yourself, on what good looks like there.
  • When you can't write down what "done" means. If you can't state acceptance criteria, the work is still an idea rather than a ticket. Ideas are great; they belong in the interactive loop.

Turning it on

/ccmagic:auto-ticket {ID} always runs autonomously. The knobs live in .claude/ccmagic.local.md per repo (with a ~/.claude/ccmagic.local.md user file for personal defaults):

needs_human_state: Blocked     # where parked tickets go
needs_human_label: needs-human # fallback label when the state doesn't exist
max_feedback_passes: 3
# max_review_fix_passes: 3
# max_validate_attempts: 2
# ci_timeout_minutes: 30

Everything autonomous is additive and opt-in: the interactive paths of every skill are unchanged, and nothing goes autonomous without an explicit signal.

What's next

Running this from my laptop is useful. Running it with no laptop involved at all is the actual goal: a Linear ticket gets a FullAuto label, a self-hosted worker picks it up in a container, and I come back later to either a merged PR and a Done ticket, or a parked ticket with a note explaining why. That's Cyrus plus ccmagic's transport layer, and it's the subject of the next post.

ccmagic is open source: github.com/devondragon/ccmagic . Install it from the Claude Code plugin marketplace with

/plugin marketplace add devondragon/ccmagic

then

/plugin install ccmagic@ccmagic

Devon
Follow