Auto complete tickets using Claude Code loop on telegram with linear MCP

singlas1 pts0 comments

An engineer you manage from a group chat โ€” Niptao

Start free build

Yesterday our operator typed this into a Telegram group:

bug: demo revision limit โ€” max revisions not being enforced

That message became a Linear ticket. The bot asked "take it? (go/skip)", got a go, announced ๐Ÿ”จ Starting NIP-153, and forty minutes later posted โœ… NIP-153 โ€” PR opened, with a link to a pull request that read like a careful engineer wrote it: a root-cause section, the fix, its assumptions stated outright, and a regression test. It's merged now. So is the one after it.

The "engineer" is a Claude Code session running in a loop on a laptop. There is no framework underneath it โ€” no OpenClaw, no LangChain, no orchestrator service, no custom harness. The system is one Markdown skill file, one 180-line Python script with no dependencies, and what Claude Code already ships: skills, subagents, git worktrees, and an MCP connection to Linear. Both files are public at singlas/ai-dev-prompts if you want to run your own.

This post is the long version: the architecture, the day-one results with real numbers, the safety model, and the three things that bit us.

The shape of it

Four pieces, each boring on its own:

The orchestrator is a skill, not a program. A Claude Code skill is a Markdown file describing a procedure; ours says: drain the Telegram group, pick the next approved ticket, triage it, delegate the build, report back, repeat. The session runs it on a self-pacing loop โ€” when every ticket is blocked waiting on a human, it schedules its own wake-up 20โ€“30 minutes out and goes quiet. The orchestrator never edits code itself. Its job is reading, deciding, and talking.

Linear is the work queue, the state machine, and the memory. More on this below โ€” it's the part people underestimate.

Telegram is the entire human interface. Questions, approvals, bug reports, results. We did not build a dashboard, and at this point I don't think we will.

Each ticket builds in its own git worktree. The orchestrator spawns a fresh subagent per ticket in an isolated worktree branched off our integration branch. The subagent implements, runs the relevant test suite and the linter, pushes, opens a PR, and reports back one paragraph. Then the worktree is gone. (Why isolation is non-negotiable when agents share a repo: we learned that one earlier.)

The part people underestimate: Linear over MCP

We wrote zero lines of issue-tracker integration. Not a small amount โ€” zero.

Claude Code talks to Linear through MCP (Model Context Protocol), the same connection our interactive sessions already use. The orchestrator lists issues by label, reads bodies and comment threads, writes comments, flips labels, and moves states using the same tools a human-driven session uses when we say "mark that ticket done." There is no webhook receiver, no REST client, no sync job, no schema to maintain. When Linear changes something, we inherit it.

That zero-integration property is what makes the rest of the design work, because we lean on the tracker for three jobs at once:

Queue. The loop works tickets in Todo carrying an agent label, by priority, oldest first.

State machine. Three labels: agent (approved to build), agent-blocked (waiting on a human answer), manual (the agent must not touch this, ever). Label transitions are the workflow โ€” there is no other state store.

Memory. Every question the agent asks in Telegram is mirrored onto the ticket as a comment, and every answer is written back the same way. The ticket carries the full conversation. Kill the loop mid-run, restart it cold tomorrow โ€” it re-reads the tickets and continues. The only state on disk anywhere is a JSON file holding a Telegram poll offset.

If you're building anything like this, that last point is the design decision we'd defend hardest: put the agent's memory where your team already looks. The audit trail isn't a log file only the agent reads โ€” it's the ticket history your humans already live in.

The Telegram side: a grammar, not an app

The bridge is a single stdlib-only Python script wrapping two Bot API calls โ€” sendMessage out, long-polled getUpdates in. No webhook server, no inbound infrastructure, nothing exposed to the internet. The whole protocol is a grammar small enough to pin in the group:

You type<br>What happens

bug:<br>Linear ticket created (labeled Bug, reporter credited) + a take it? (go/skip) proposal

feature: โ€ฆ / ticket: โ€ฆ<br>Same, labeled Feature / unlabeled

go (reply to a proposal)<br>The agent label is applied โ€” approved to build

take NIP-123<br>Green-light an existing ticket directly

Reply to a โ“, or NIP-123<br>Answer lands on the ticket as a comment; it unblocks

And the agent talks back: โ“ batched clarifying questions, ๐Ÿ”จ when a build starts, โœ… with the PR link, โš ๏ธ when it gives up on a ticket.

Here's what that grammar changed organizationally. Before: operator notices a bug โ†’ messages the founder โ†’ founder writes a ticket โ†’ an engineering session eventually picks it up โ†’ clarifying...

ticket agent linear telegram code build

Related Articles