{"slug": "building-a-software-factory-two-cursor-cloud-agents-that-take-asana-tickets-to", "title": "Building a software factory: two Cursor cloud agents that take Asana tickets to production (AI Tinkerers talk, Rating Captain)", "summary": "Mateusz Piwnicki, speaking at AI Tinkerers, detailed a \"software factory\" in which two Cursor cloud agents, Roman and Bogdan, take Asana tickets through planning, coding, independent review, merge, deploy and live verification. Across 259 opened pull requests, 232 were merged, 208 of them by the agents after an independent review and written merge gate, at a computed cost of about $7 per merged PR on IdeaShirt. Piwnicki said the setup leaves him roughly 10 hours of his own engineering per week on top of running the factory.", "body_md": "You're welcome to use this. If it becomes useful, credit [Mateusz Piwnicki](https://www.linkedin.com/in/mateuszpiwnicki/).\n\nTwo Cursor cloud agents take Asana tickets all the way to production: plan, code, independent review, merge, deploy, live check, hand back. This gist is the sanitised playbook behind the AI Tinkerers talk by Mat (Rating Captain, IdeaShirt, Yax Software).\n\n- **Roman** works on Rating Captain, a local-SEO SaaS (Laravel, several apps in one monorepo).\n- **Bogdan** works on IdeaShirt, an e-commerce store, plus two acquired brands with their legacy code.\n\nAccount ids, server names, Asana ids, emails, and customer names are replaced with placeholders.\n\n| PRs opened | 259 | \n| Merged | 232 | \n| Merged by the agents, after independent review and a written merge gate | 208 | \n| Merged by a human, after review | 24 | \n| Computed cost per merged PR (IdeaShirt, two-week median) | about $7, typically two agent runs | \n| My own engineering per week, on top of running the factory | about 10 hours | \n\n1. **Intake.** A ticket is assigned to the agent's Asana user. A scheduled Cursor Automation (the dispatcher) checks the inbox and decides: start, resume, or skip. It never writes code.\n2. **Work.** One Cursor cloud agent per ticket, started with`POST https://api.cursor.com/v1/agents` . Grok 4.7 builds. Opus designs the UX and reviews the code. The reviewer never sees the author's reasoning.\n3. **Ship.** PR, merge gate, deploy, check the live URL, hand back in Asana with a comment.\n\nBoth agents:\n\n- **Cursor Cloud Agents.** One VM per ticket, started by the dispatcher through the Cloud Agents API.\n- **Asana.** Each agent is a real Asana user. It takes tickets, comments, reassigns, adds tags, uploads files.\n- **GitHub and GitHub Actions.** Branch, PR, review, merge. Each app has its own deploy workflow. A backup token covers auth failures.\n- **AWS.** Cursor's short-lived identity token is exchanged for a one-hour IAM role. Production access is SSM Run Command on three named servers. No VPN, no stored keys. See`03-aws-trust-policy.json` and`04-aws-permissions-policy.json` .\n- **Cloudflare.** Cache checks, and a purge skill for when stale pages survive a deploy.\n- **Models.** Grok 4.7 (standard speed, never Fast) does the work. Opus designs the UX and reviews the code. Grok checks the live site.\n\nBogdan, the e-commerce agent, also has:\n\n- **Search Console, GA4, Google Ads, Meta Ads.** Our own MCP servers on Cloudflare Workers, behind a bearer token and an IP allowlist. The dispatcher attaches them on every run.\n- **Google Tag Manager.** A skill on the Tag Manager API that audits the containers.\n- **Product feeds** for Google Merchant Center, Facebook, and TikTok.\n- **Production database.** Read-only queries through SSM. After a merge he checks that the merged SQL script is applied before handing the ticket over for testing.\n- **Live checks behind Cloudflare.** A secret cookie lets his QA browser past bot protection without turning it off.\n- **One Asana board, three brands.** The dispatcher routes each ticket to the right repo.\n\n- **MCP** for data you own and host, mostly read: analytics, ads, Search Console. One server serves humans and agents. Our database MCP servers accept only`SELECT` ,`SHOW` ,`DESCRIBE` ,`EXPLAIN` .\n- **Plain API or CLI** for the agent's own identity and actions: Asana as the agent, GitHub, AWS, Cloudflare.\n\nTraps we hit:\n\n- An MCP OAuth login is stored once per server URL. Our second Asana workspace silently reused the first workspace's login. The agent in that workspace now uses a personal access token over REST.\n- MCP servers passed when an agent is created are not kept on follow-ups. Send them on every run.\n- Every MCP tool description sits in the agent's context on every turn. A curl example inside a skill costs nothing until the agent opens it.\n\n- Fast mode is the default on paid plans and costs about twice as much. Send `params: [{\"id\":\"fast\",\"value\":\"false\"}]` on**every create and every follow-up** . A follow-up without a model goes back to the default.\n- A subagent started without an explicit model inherits the parent's model, Fast included. Name the model on every subagent call.\n- Every follow-up re-reads the whole conversation. Wake an agent only when a human did something new on the ticket. Never send \"continue working\".\n- When the latest run's `cacheReadTokens` reaches 8,000,000, archive the agent and start a fresh one on the same branch and PR from an ~80-line brief file kept on the branch. Memory lives in the repo, not the chat.\n\nEffect on IdeaShirt: median computed cost per merged PR went from about $18 (Fast week) to about $10, then about $7 after the long-thread handoff. In one week, 13 PRs averaged $6.53 and one old, long thread cost $60.15.\n\n- **\"I can't\" requires evidence.** An agent kept replying \"no VPN, I can't check production\" two days after it was given production access, because the first sentence of its rule said \"this VM is not on the VPN\". It may now only say it can't reach something after the command fails, quoting the error.\n- **One repair, then escalate.** A one-line fix hit an unrelated upstream build failure. \"Stay until the deploy is green\" plus \"fix the cause\" produced nine follow-up PRs, and a human merged seven of them. Now: a \"not my fault\" test, one repair per task, then stop with a`Build failed!` comment assigned to a human.\n- **Merge rights by what could break, not by diff size.** A one-line billing change waits for a human. A 500-line bug fix doesn't. Human-only: money, login, SEO, infrastructure, migrations, customer emails, ads tracking, new flows. When unsure, it's not safe.\n- **Green pipeline is not done.** Done is the live URL working after every deploy step, including after-scripts.\n\n- `01-worker-rule.md` : the loop each ticket agent follows.\n- `02-dispatcher.md` : the scheduled dispatcher, with the create and follow-up calls.\n- `03-aws-trust-policy.json` : IAM trust for Cursor's OIDC identity.\n- `04-aws-permissions-policy.json` : SSM Run Command on three tagged servers, plus log queries.\n\nRead-only on production is a rule in the prompt, not an IAM permission: `AWS-RunShellScript` runs as root. If you copy this, lock it down with a custom SSM document.", "url": "https://wpnews.pro/news/building-a-software-factory-two-cursor-cloud-agents-that-take-asana-tickets-to", "canonical_source": "https://gist.github.com/piwni/33c5437976a0e34a973488e7cc3d8d7e", "published_at": "2026-11-03 11:58:01+00:00", "updated_at": "2026-11-04 17:52:10.829654+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "mlops", "ai-products"], "entities": ["Cursor", "Asana", "GitHub", "AWS", "Cloudflare", "Mateusz Piwnicki", "Rating Captain", "IdeaShirt"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/building-a-software-factory-two-cursor-cloud-agents-that-take-asana-tickets-to", "markdown": "https://wpnews.pro/news/building-a-software-factory-two-cursor-cloud-agents-that-take-asana-tickets-to.md", "text": "https://wpnews.pro/news/building-a-software-factory-two-cursor-cloud-agents-that-take-asana-tickets-to.txt", "jsonld": "https://wpnews.pro/news/building-a-software-factory-two-cursor-cloud-agents-that-take-asana-tickets-to.jsonld"}}