{
    "componentChunkName": "component---src-templates-post-js",
    "path": "/blog/building-my-own-agent-sandbox",
    "result": {"data":{"site":{"siteMetadata":{"siteUrl":"https://www.tms-doan.com"}},"markdownRemark":{"html":"<p>In my last post, I wrote that agents can write code, but we still own the system.</p>\n<p>I meant that.</p>\n<p>But I also kept thinking about the practical part.</p>\n<p>What if the agent did not have to stop when I close my laptop?</p>\n<p>What if it had a real place to work?</p>\n<p>Not a disposable sandbox that wakes up, changes three files, and disappears.\nA persistent environment where several agents can investigate tickets, run tests, write documentation, and keep moving work forward.</p>\n<p>That is what I am building now.</p>\n<p>Not an autonomous company in a box.\nNot a robot CTO.</p>\n<p>More like a small engineering workspace that happens to have agents working in it most of the time.</p>\n<hr>\n<h3>The basic idea</h3>\n<p>The idea is pretty simple.</p>\n<ul>\n<li>a server in a data center that stays online</li>\n<li>one isolated workspace per repository or task</li>\n<li>a queue of work coming from Jira</li>\n<li>several agents with clear jobs instead of one giant agent doing everything</li>\n<li>tests, checks, and limits around every meaningful action</li>\n<li>a GitHub Copilot review on every pull request</li>\n<li>Slack updates that keep me informed without asking me to watch the system all day</li>\n<li>a clear path to ask a human for help when the agents are stuck</li>\n</ul>\n<p>The server is the home.\nThe workspace is the desk.\nJira is the inbox.\nGitHub is the paper trail.\nSlack is the window into what is happening.</p>\n<p>This is the rough loop in my head:</p>\n<div class=\"agent-flow\" role=\"img\" aria-label=\"Workflow from a Jira ticket through planning, implementation, and verification, ending in either a reviewed pull request or a GitHub issue and Slack notification when human input is needed.\">\n  <div class=\"agent-flow__stages\">\n    <div class=\"agent-flow__stage\">\n      <span class=\"agent-flow__number\">01</span>\n      <strong>Jira ticket</strong>\n      <span>Agent-ready work enters the queue</span>\n    </div>\n    <span class=\"agent-flow__arrow\" aria-hidden=\"true\">→</span>\n    <div class=\"agent-flow__stage\">\n      <span class=\"agent-flow__number\">02</span>\n      <strong>Plan and build</strong>\n      <span>An isolated agent workspace does the work</span>\n    </div>\n    <span class=\"agent-flow__arrow\" aria-hidden=\"true\">→</span>\n    <div class=\"agent-flow__stage\">\n      <span class=\"agent-flow__number\">03</span>\n      <strong>Verify</strong>\n      <span>Tests and an independent check</span>\n    </div>\n  </div>\n  <div class=\"agent-flow__outcomes\">\n    <div class=\"agent-flow__outcome agent-flow__outcome--success\">\n      <span>When it passes</span>\n      <strong>Pull request → Copilot review → me</strong>\n    </div>\n    <div class=\"agent-flow__outcome agent-flow__outcome--blocked\">\n      <span>When it gets stuck</span>\n      <strong>GitHub issue → Slack update → me</strong>\n    </div>\n  </div>\n</div>\n<p>That last part matters more than it sounds.</p>\n<p>If an agent cannot resolve a ticket after a reasonable number of attempts, I do not want it to keep having an existential crisis in the terminal for six hours.</p>\n<p>I want it to collect what it learned, create a GitHub issue with the evidence, link it back to Jira, and move on.</p>\n<p>At least then a dead end becomes useful work.</p>\n<hr>\n<h3>Anthropic has a different constraint</h3>\n<p>I was reading <a href=\"https://newsletter.pragmaticengineer.com/p/inside-anthropic\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">this great look inside Anthropic</a>, and it made the difference very clear.</p>\n<p>People there reportedly run several agents in parallel all the time. Token usage is not treated like a personal budget. One large rewrite used 64 parallel agents and around $165,000 worth of tokens at API price.</p>\n<p>That is a pretty wild picture of where this can go.\nIt is also very much not my situation.</p>\n<p>I do not have unlimited tokens.\nMost teams do not.</p>\n<p>So I do not want to copy the surface-level behavior and run ten agents on every tiny ticket just because an AI lab can.\nFor me, every agent run has a cost. Context has a cost. A retry has a cost. Even a bad plan has a cost when three agents spend an afternoon following it.</p>\n<p>That changes the design.</p>\n<p>My system needs to get good at deciding what <em>not</em> to run.</p>\n<p>A tiny, well-defined bug might get one implementation agent and one verifier. A larger investigation might get a short, capped research pass before anyone edits code. A vague ticket should come back with questions, not consume the monthly budget trying to guess what somebody meant.</p>\n<p>The goal is not maximum agent activity.\nThe goal is the highest amount of useful, verified work per dollar and per unit of human attention.</p>\n<hr>\n<h3>How I would build it</h3>\n<p>I would start way smaller than people expect.</p>\n<p>First, I would create one persistent machine with access only to the repositories, credentials, and services it genuinely needs.\nNo production database access by default.\nNo long-lived admin keys sitting in a folder because “the agent needs them.”\nNo direct path from a Jira ticket to production.</p>\n<p>Then I would give every task its own isolated worktree or container.</p>\n<p>This matters because agents are fast, but they are also very enthusiastic.\nIf three agents are all changing the same branch, you do not have a multi-agent system.\nYou have a group project with no adult in the room.</p>\n<p>Each workspace should have:</p>\n<ul>\n<li>a clean branch</li>\n<li>the project instructions and useful product context</li>\n<li>a restricted set of secrets</li>\n<li>a time and cost budget</li>\n<li>a maximum number of retries and parallel agents</li>\n<li>a way to save logs, test results, and the agent's final summary</li>\n</ul>\n<p>Then I would add a small orchestrator. It does not need to be magical. It just needs to keep things moving.</p>\n<p>Its job is to:</p>\n<ol>\n<li>Read eligible tickets from Jira.</li>\n<li>Turn a ticket into a clear task with acceptance checks.</li>\n<li>Create an isolated workspace.</li>\n<li>Assign the work to the right agent.</li>\n<li>Run the tests and quality gates after the agent finishes.</li>\n<li>Open a pull request when the result is ready for review.</li>\n<li>Request a GitHub Copilot review on that pull request.</li>\n<li>Escalate with evidence when it is not.</li>\n</ol>\n<p>The orchestrator should be boring.\nThat is a compliment.</p>\n<p>I do not need it inventing a new architecture at 3 a.m. I need it to keep track of work, protect the boundaries, and make the next decision obvious.</p>\n<hr>\n<h3>Give the agents different jobs</h3>\n<p>I do not think one general-purpose agent should own the whole loop.</p>\n<p>For a small system, I would use roles like these:</p>\n<ul>\n<li>a planner that reads the ticket, repository context, and acceptance criteria</li>\n<li>an implementer that makes the change in an isolated branch</li>\n<li>a reviewer that looks for risky assumptions, missing tests, and scope drift</li>\n<li>a verifier that runs the real checks and investigates failures</li>\n<li>a triage agent that writes a useful escalation when the work cannot continue</li>\n</ul>\n<p>They do not need fake personalities or a weird little org chart.\nThe point is to make responsibility clear.</p>\n<p>The planning agent should not quietly approve its own guess.\nThe implementation agent should not decide that failing tests are somebody else's problem.\nAnd the triage agent should not create a GitHub issue that says only: “agent got stuck, please help.”</p>\n<p>That issue needs to be useful to a human.</p>\n<p>Every pull request should also get a GitHub Copilot review.</p>\n<p>That gives the system a fresh set of eyes outside the implementation context. The agent that wrote the code is usually very good at explaining why its own choices make sense. A reviewer is useful because it can push back.</p>\n<p>Copilot review is not a magic green stamp. It is another quality gate. Comments need to be addressed, tests still need to pass, and a human still owns the merge decision when there is real risk.</p>\n<hr>\n<h3>The Jira to GitHub handoff</h3>\n<p>This is the workflow I want to try.</p>\n<p>A Jira ticket enters the system only when it is marked as safe for agent work.\nMaybe it has a label like <code class=\"language-text\">agent-ready</code>, clear acceptance criteria, and no production or security risk.</p>\n<p>The planner turns that into a task. The implementation agent works on it. The verifier checks the result.</p>\n<p>If everything passes, the system opens a pull request, requests a GitHub Copilot review, and posts a short summary back on the Jira ticket:</p>\n<ul>\n<li>what changed</li>\n<li>what was tested</li>\n<li>what assumptions were made</li>\n<li>the pull request link</li>\n<li>the Copilot review status</li>\n</ul>\n<p>If the agent fails, it gets a limited retry with fresh context. Maybe another agent reviews the failure before it tries again.</p>\n<p>But retries need a limit.</p>\n<p>After that, the triage agent creates a GitHub issue and links it to the original Jira ticket. The issue should include:</p>\n<ul>\n<li>the original goal and acceptance criteria</li>\n<li>what the agents tried</li>\n<li>relevant logs and failing test output</li>\n<li>the suspected blocker</li>\n<li>the exact decision or access a human needs to provide</li>\n<li>links to the branch, commits, and Jira ticket</li>\n</ul>\n<p>Then Jira can move to something like <code class=\"language-text\">Needs Human Decision</code> instead of pretending the work is still actively progressing.</p>\n<p>That is a small detail, but I think it changes the whole feeling of the system.</p>\n<p>The agent is not hiding failure.\nIt is packaging failure so the team can act on it.</p>\n<hr>\n<h3>Slack is how I stay close to the work</h3>\n<p>I do not want to spend my day refreshing Jira, GitHub, and some server dashboard just to see whether the agents are doing anything useful.</p>\n<p>So the system should send useful updates to Slack.</p>\n<p>Not every tool call. Not every thought an agent has. That would get annoying immediately.</p>\n<p>I want updates for the moments that change the state of the work:</p>\n<ul>\n<li>an agent claims a Jira ticket</li>\n<li>a pull request is ready, with its test and Copilot review status</li>\n<li>a review finds something that needs another pass</li>\n<li>a task is blocked and a GitHub issue was created</li>\n<li>a human decision, approval, or clarification is needed</li>\n<li>a short daily summary of completed work, cost, failures, and the active queue</li>\n</ul>\n<p>Each Slack message should link back to the Jira ticket, GitHub pull request or issue, and the relevant run summary.</p>\n<p>That is enough for me to understand what happened and decide where my attention actually matters. I can open the details when I need them, instead of turning Slack into a live transcript of agents talking to themselves all day.</p>\n<hr>\n<h3>Make budget part of the workflow</h3>\n<p>I would put the budget rules directly into the orchestrator instead of hoping everybody remembers them.</p>\n<p>Every ticket gets a small initial allowance: perhaps one planning pass, one implementation pass, one independent verification pass, and one retry if the failure is concrete.</p>\n<p>The system should stop when it hits the allowance, not quietly keep spending because the agent sounds very confident.</p>\n<p>For more important work, I can explicitly raise the budget. That is a human decision, like approving more time for an engineer to investigate a difficult problem.</p>\n<p>I would also keep a simple scoreboard:</p>\n<ul>\n<li>token and infrastructure cost per ticket</li>\n<li>pull requests accepted versus rejected</li>\n<li>number of retries before success</li>\n<li>escalations that led to a real human decision</li>\n<li>repeated failure patterns</li>\n</ul>\n<p>That is how the system gets better over time.</p>\n<p>Maybe a verifier is catching problems early and is worth its cost. Maybe a certain class of Jira tickets almost always needs product clarification. Maybe two agents are redundant for small changes.</p>\n<p>Without that feedback, “always on” can become “always spending.”</p>\n<hr>\n<h3>What I would not automate yet</h3>\n<p>There are a few things I would keep human-owned from the beginning.</p>\n<ul>\n<li>changing production infrastructure</li>\n<li>merging to protected branches</li>\n<li>deploying without an explicit approval step</li>\n<li>changing permissions or secrets</li>\n<li>closing customer-impacting incidents</li>\n<li>deciding that a vague ticket is “good enough” to build</li>\n</ul>\n<p>Could an agent eventually help with all of these? Of course.</p>\n<p>But “can help” and “should decide alone” are very different things.</p>\n<p>The system earns more autonomy through evidence.\nIf it consistently handles a class of work well, we can widen the boundary. If it creates confusing pull requests or repeats the same mistake, we tighten it again.</p>\n<p>That feels healthier than declaring the whole engineering process autonomous on day one.</p>\n<hr>\n<h3>The real challenge is not the server</h3>\n<p>Setting up a machine that stays on is not the hard part.</p>\n<p>The hard part is making sure the agents have enough context to do useful work without giving them a giant pile of outdated notes, random credentials, and permission to create chaos.</p>\n<p>Context needs maintenance.</p>\n<p>The repository should explain how to run, test, and deploy the project.\nImportant decisions should live somewhere the agent can find them.\nTickets need clear definitions of done.\nAnd the system needs observability just like any other production system:</p>\n<ul>\n<li>which tickets are being worked on</li>\n<li>how long they take</li>\n<li>how often they pass tests</li>\n<li>how often they need human help</li>\n<li>how much each completed task costs</li>\n<li>which failures keep coming back</li>\n</ul>\n<p>Without that, you are not running an agent platform.\nYou are just leaving an expensive computer unsupervised.</p>\n<hr>\n<h3>Why I am excited about it</h3>\n<p>I am not excited because I want to remove people from the loop.</p>\n<p>I am excited because there is so much valuable work that gets delayed by waiting.</p>\n<p>Waiting for someone to run the first investigation.\nWaiting for a reproducible bug report.\nWaiting for a branch with the obvious cleanup.\nWaiting for a first pass at documentation.</p>\n<p>An always-on agent workspace can make those waiting periods smaller. That is the part I find exciting.</p>\n<p>I can come back in the morning and review a real pull request, a clear question, or a well-written escalation instead of starting from an empty ticket.</p>\n<p>That is the version of agentic work I want.</p>\n<p>More momentum, yes.\nBut also more traceability.\nMore experiments, but better boundaries.\nAgents working full time, while humans stay responsible for what the system means and what it ships.</p>\n<p>My laptop does not have to be running.</p>\n<p>I still need to be paying attention.</p>","timeToRead":10,"frontmatter":{"title":"Building My Own Agent Sandbox","description":"How I am building a persistent sandbox environment for AI coding agents, with a human escalation path when they get stuck","date":"2026-08-21T00:00:00.000Z","updated":null,"slug":"/blog/building-my-own-agent-sandbox","tags":["AI","Engineering","Product","Culture"]}}},"pageContext":{}},
    "staticQueryHashes": ["1672341955","2009693873","2077189868","3505212370","3825832676"]}