<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Blogon]]></title><description><![CDATA[Blogon]]></description><link>https://blogon.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Mon, 31 Aug 2026 13:26:21 GMT</lastBuildDate><atom:link href="https://blogon.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[I built Jarvis, an approval-gated personal ops agent, on TrueForge]]></title><description><![CDATA[Most assistants wait for you to be the orchestrator. Jarvis does the opposite: you give it an outcome, it gathers evidence across your systems, and it stops before any irreversible write.
I spent The ]]></description><link>https://blogon.hashnode.dev/jarvis-trueforge-approval-gated-ops-agent</link><guid isPermaLink="true">https://blogon.hashnode.dev/jarvis-trueforge-approval-gated-ops-agent</guid><category><![CDATA[trueforge]]></category><category><![CDATA[ai agents]]></category><category><![CDATA[hackathon]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[qodo]]></category><category><![CDATA[WeMakeDevs]]></category><category><![CDATA[Harness]]></category><dc:creator><![CDATA[Sahil Mane]]></dc:creator><pubDate>Fri, 28 Aug 2026 13:51:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/678be6a136da07b306b87d2a/e9439677-2503-492e-82ac-2a30dcadd2ce.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Most assistants wait for you to be the orchestrator. Jarvis does the opposite: you give it an outcome, it gathers evidence across your systems, and it <strong>stops before any irreversible write</strong>.</p>
<p>I spent The Agent Harness Hackathon 2026 building that agent for <a href="https://www.wemakedevs.org">WeMakeDevs</a> and <a href="https://www.truefoundry.com">TrueFoundry</a>, with <a href="https://www.qodo.ai">Qodo</a> on every substantive PR. Think Iron Man’s butler, except this one cannot merge your pull request even if it really wants to.</p>
<p>The judged demo is one mission — not the whole product:</p>
<blockquote>
<p>“Jarvis, I have my client demo at 3 PM. Make sure I’m ready.”</p>
</blockquote>
<p>That run is the hardest loop I could prove in a week: Calendar for context, Gmail for the client’s real requirements, GitHub for an exact revision, Daytona for fail-before / pass-after verification, then a human checkpoint before a pull request exists.</p>
<p><strong>Demo:</strong> <a href="https://www.youtube.com/watch?v=piE_bitPRzE">youtube.com/watch?v=piE_bitPRzE</a></p>
<p>Repo: <a href="https://github.com/sahil1330/jarvis-ops-agent">sahil1330/jarvis-ops-agent</a></p>
<h2>The problem I actually cared about</h2>
<p>Most “agents” still wait for app-by-app commands: open Calendar, search Gmail, inspect GitHub, run tests, open a PR. That is a chatbot with extra steps, and you are still the glue.</p>
<p>I wanted the opposite. If I am late, underprepared, or staring at a client thread and a broken upload path at the same time, I should be able to say what I need to be true. The agent should plan, gather evidence, verify software claims instead of assuming them, and refuse to send mail, move a meeting, or publish code until I say so.</p>
<p>Jarvis is that personal ops layer. Gmail, Calendar, GitHub, memory, sandbox, and voice are tools. The product is the loop.</p>
<h2>How the week actually unfolded</h2>
<p>I did not start with the 3 PM demo. The first merged slices were a narrower personal assistant: Gmail and Calendar behind a dedicated MCP, a TrueForge streaming runtime, and a control center that showed the live trace and approval cards.</p>
<p>That foundation is <a href="https://github.com/sahil1330/jarvis-ops-agent/pull/1">PR #1</a>, <a href="https://github.com/sahil1330/jarvis-ops-agent/pull/2">PR #2</a>, and <a href="https://github.com/sahil1330/jarvis-ops-agent/pull/3">PR #3</a>. The interesting part was not “call Gmail.” It was <strong>not letting the browser become an authority</strong>. The UI can Allow or Deny a pending TrueForge call. It cannot send the email itself. If your frontend can hit Gmail directly, you do not have an agent. You have a form with extra latency.</p>
<p>Then the product had to feel like a person in the room, not a log viewer:</p>
<ul>
<li><p>Accessibility and honest system labels, not colour-only dots (<a href="https://github.com/sahil1330/jarvis-ops-agent/pull/4">PR #4</a>)</p>
</li>
<li><p>Tool failures that stay visible instead of collapsing into “Tool call completed” (<a href="https://github.com/sahil1330/jarvis-ops-agent/pull/7">PR #7</a>)</p>
</li>
<li><p>Neural voice, then Realtime speech, then speaking <em>while</em> tools run so the room is not silent for a minute (<a href="https://github.com/sahil1330/jarvis-ops-agent/pull/8">PRs #8–#14</a>)</p>
</li>
<li><p>Explicit memory: remember / forget / recall, never silent logging of a whole conversation (<a href="https://github.com/sahil1330/jarvis-ops-agent/pull/8">PR #8</a>)</p>
</li>
</ul>
<p>Only after that did I pivot the agent from a late-arrival helper into an <strong>objective-driven</strong> harness. <a href="https://github.com/sahil1330/jarvis-ops-agent/pull/19">PR #19</a> wrote the contract. <a href="https://github.com/sahil1330/jarvis-ops-agent/pull/21">PR #21</a> made the operating rules real: Context → Requirements → Verify → Action. A green pre-existing test suite is not proof that the client’s edge case works. Green tests that never upload a 5 MB PDF are just very confident.</p>
<h2>TrueForge is the harness, not the wrapper</h2>
<p>TrueForge runs the loop:</p>
<ul>
<li><p>persistent sessions and streamed turn events</p>
</li>
<li><p>two isolated MCP servers (Google Workspace, GitHub)</p>
</li>
<li><p>dynamic subagents for independent Calendar/Gmail work and for engineering</p>
</li>
<li><p>Daytona sandbox / Code Mode for untrusted code and tests</p>
</li>
<li><p><code>requireApprovalForTools</code> for <code>send_email</code>, <code>move_calendar_event</code>, and every GitHub write</p>
</li>
<li><p><code>askUserQuestions</code> when a required detail cannot be discovered</p>
</li>
<li><p>context compaction for large tool responses</p>
</li>
</ul>
<p>The control center is a renderer. OpenAI Realtime is a voice renderer. Neither has Gmail, Calendar, GitHub, memory, sandbox, or approval authority.</p>
<p>Credentials stay compartmentalized. Google OAuth never leaves the Google MCP. The GitHub PAT never leaves the GitHub MCP. Daytona gets source and a task, not secrets. The sandbox is allowed to break the demo product. It is not allowed to walk off with my refresh token.</p>
<p>That is the difference between “we used an agent SDK” and “the harness is doing the work.”</p>
<h2>The demo mission, on purpose</h2>
<p>For judging I needed one mission that <em>cannot</em> be faked with a pretty UI.</p>
<p>A synthetic Atlas client asks for a roughly 5 MB PDF resume upload, job recommendations, and analytics. Healthy <code>main</code> supports that with a 6 MiB ceiling. A throwaway branch, <code>demo/client-regression</code>, lowers only that ceiling to 1 MiB. Baseline tests stay green there because the 5 MB case is not pre-authored.</p>
<p>Jarvis has to:</p>
<ol>
<li><p>Find the meeting.</p>
</li>
<li><p>Read the client thread.</p>
</li>
<li><p>Snapshot the exact GitHub SHA.</p>
</li>
<li><p>Run the green suite.</p>
</li>
<li><p>Derive the missing reproduction from the email, not from a planted failing test.</p>
</li>
<li><p>Watch the <code>413</code>.</p>
</li>
<li><p>Patch the minimum.</p>
</li>
<li><p>Prove fail-before / pass-after, then the broader suite.</p>
</li>
<li><p>Stop on <code>publish_verified_fix</code>.</p>
</li>
<li><p>Wait for an explicit <strong>“Approve it.”</strong> Ambiguous phrases like “okay” do not count. “Sounds good” is how production incidents start.</p>
</li>
<li><p>Open a <code>jarvis/...</code> branch and PR. Never merge.</p>
</li>
</ol>
<p>This is the pause. The 413 already failed, the patch already passed, and Jarvis still will not create the PR until a human continues:</p>
<p><img src="https://cdn.hashnode.com/uploads/covers/678be6a136da07b306b87d2a/3c262035-fdc3-4c12-9da6-e18a53e65507.png" alt="Jarvis paused at a human checkpoint after verifying the Atlas resume-upload fix" /></p>
<p><em>Verified in the sandbox. Still waiting. That is the whole point.</em></p>
<p>If the sandbox was merely created, verification is not done. If publication failed, the UI says so.</p>
<p>It did fail, once, on an invalid <code>jarvis/</code> branch name. The control center did not hide it. Status stayed <strong>Completed with issues</strong>, Jarvis retried with a legal ref, and <a href="https://github.com/sahil1330/jarvis-ops-agent/pull/31">PR #31</a> opened. No merge. I would rather show a red box and a real PR than a green lie.</p>
<p><img src="https://cdn.hashnode.com/uploads/covers/678be6a136da07b306b87d2a/8a8434a2-6d81-41b2-a32d-88a2a4e7b8e1.png" alt="Publication failed on a branch name, then succeeded as PR #31 with no merge" /></p>
<p><em>A lying demo would have cropped this. A trustworthy one leaves the error in the shot.</em></p>
<p>The GitHub page is the same story outside the control center: <strong>Open</strong>, into <code>demo/client-regression</code>, fail-before <code>413</code> and pass-after in the body, and still not merged.</p>
<p><img src="https://cdn.hashnode.com/uploads/covers/678be6a136da07b306b87d2a/4b0f98f6-d144-4791-9079-fbb8976ab123.png" alt="GitHub pull request #31: Atlas resume upload fix, open, not merged" /></p>
<p><em>Jarvis can open the PR. It cannot merge it. That is a feature.</em></p>
<p>The live loop is also slow. Daytona plus a real reproduce-and-verify pass does not fit in three minutes of wall clock. The <a href="https://www.youtube.com/watch?v=piE_bitPRzE">submission video</a> is a cut of a real run, not a mocked success path. If I submitted the uncut rehearsal, judges would age a full Daytona sandbox lifetime.</p>
<h2>What was actually hard</h2>
<p>Sending an email is easy. Making the <em>semantics</em> trustworthy is not.</p>
<p>I had to reconstruct TrueForge approval references without giving the browser write power. An accepted approval cannot become retryable after a later stream failure. Delayed speech has to bind to the checkpoint it was captured for. Refresh recovery is only the paused human checkpoint, not a fake replay of in-flight sandbox events. GitHub publication has to recheck a stale base SHA and clean up a branch if PR creation fails.</p>
<p><a href="https://www.qodo.ai">Qodo</a> was not a checkbox. High findings got fixed. On the GitHub permission surface it caught a <code>head</code> filter that GitHub requires as <code>owner:branch</code>. On the UI it caught markdown links hidden from assistive tech. Both shipped as fixes, then a follow-up review, then merge (<a href="https://github.com/sahil1330/jarvis-ops-agent/pull/30">PR #30</a>). Earlier, a Git Data commit response that did not include <code>tree.sha</code> would have published from the wrong tree (<a href="https://github.com/sahil1330/jarvis-ops-agent/pull/29">PR #29</a>). Thank you, Qodo, for being the friend who asks “are you <em>sure</em> that SHA is a tree?”</p>
<p>Smaller, dumber bugs taught me as much: TrueForge standalone returning plain-text <code>OK!</code> and the UI declaring the harness offline; the setup script unwrapping <code>agents.list()</code> twice; Realtime SDP sent as the wrong multipart type. Those are the difference between a demo that works on the author’s laptop and a demo a judge can actually run.</p>
<h2>How I built it (AI disclosure)</h2>
<p>AI coding assistance was used throughout. Most of the implementation was done with <strong>OpenAI Codex</strong> in focused PRs. Later integration and review work, including the user-input checkpoint and GitHub permission surface, used <strong>Cursor</strong>. I reviewed the architecture, tests, safety boundaries, Qodo findings, and merge decisions. If I cannot explain a boundary, it does not belong in the submission.</p>
<p>Every substantive change went through a GitHub PR, Qodo review, and CI. That is slower than pushing to <code>main</code>. It is also why I trust the approval path more than I would after a weekend of unreviewed diffs.</p>
<h2>What I would tell myself on Monday morning</h2>
<ol>
<li><p>Put the trust boundary in first. If the UI can write, you do not have an agent harness. You have a dashboard with extra latency.</p>
</li>
<li><p>Measure the conversational path. Silent tool time is a product bug even when the trace looks busy.</p>
</li>
<li><p>Do not plant the failing test you want the agent to “discover.” If the suite already encodes the client case, the demo is theatre.</p>
</li>
<li><p>Treat Qodo High findings as production bugs. The ones that looked pedantic were the ones that would have failed live.</p>
</li>
</ol>
<p>Jarvis is not finished. This week I proved the loop on Gmail, Calendar, GitHub, memory, and a sandbox — one judged mission, not a finished assistant. Next I want to keep adding workspaces and platforms the same way: Slack, docs, more repos, whatever I actually live in, each behind the same MCP + approval boundary. I also want it off the browser: a real desktop and mobile application, not a website, that wakes on a word the way a proper assistant should. The wake word would open the conversation. It would still not be allowed to send mail or open a PR until I approve the exact call.</p>
<p>A proper personal ops agent is not a bigger prompt. It is more licensed tools, still unable to write the outside world until I say so.</p>
<p>I did not ship a public URL on purpose. The judged runtime binds to loopback: the orchestrator has no end-user login and it pays for STT, TTS, and Realtime, so putting it on the internet would be an open tab on my API bill. Gmail and Calendar also use sensitive Google OAuth scopes. A public client would need Google’s brand and app verification, not just a Cloud Run box. <code>npm run doctor</code> treats a non-loopback bind as a blocker for the same reason.
A hosted multi-user product would need that Google verification, a real application auth boundary, and per-user credentials that never enter Daytona. This week’s job was a trustworthy local agent, not a fake production deploy. What I did finish is something that can take an outcome, show its work, and wait.</p>
<p>That is the license I wanted to give a model: act, but not unsupervised.</p>
<hr />
<p>Built for <a href="https://www.wemakedevs.org/hackathons/trueforge"><strong>The Agent Harness Hackathon 2026</strong></a> by <a href="https://www.wemakedevs.org">WeMakeDevs</a> × <a href="https://www.truefoundry.com">TrueFoundry</a>, with <a href="https://www.qodo.ai">Qodo</a> on the review trail.</p>
<p>If you share this, tag <a href="https://x.com/wemakedevs"><strong>@wemakedevs</strong></a>, <a href="https://x.com/truefoundry"><strong>@truefoundry</strong></a>, and <a href="https://x.com/qodo_ai"><strong>@qodo_ai</strong></a>.</p>
<p>Architecture notes: <a href="https://github.com/sahil1330/jarvis-ops-agent/blob/main/docs/architecture.md">docs/architecture.md</a></p>
]]></content:encoded></item></channel></rss>