<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Evan Green</title>
    <description>The latest articles on DEV Community by Evan Green (@tacshade).</description>
    <link>https://dev.to/tacshade</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3785580%2Fe6dcafbb-c385-4e04-9e48-23bf709f4b5e.png</url>
      <title>DEV Community: Evan Green</title>
      <link>https://dev.to/tacshade</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tacshade"/>
    <language>en</language>
    <item>
      <title>Why AI Agents Fall Apart on Real Work</title>
      <dc:creator>Evan Green</dc:creator>
      <pubDate>Wed, 18 Mar 2026 12:36:35 +0000</pubDate>
      <link>https://dev.to/tacshade/why-ai-agents-fall-apart-on-real-work-457p</link>
      <guid>https://dev.to/tacshade/why-ai-agents-fall-apart-on-real-work-457p</guid>
      <description>&lt;p&gt;I've been learning the hard way that building real autonomous AI systems has very little to do with writing better prompts.&lt;/p&gt;

&lt;p&gt;I am building &lt;a href="https://github.com/frumu-ai/tandem" rel="noopener noreferrer"&gt;Tandem&lt;/a&gt;, an open-source autonomous execution engine designed for long-running work. The goal is simple: take on a mission, move through structured tasks, and only advance when the work is actually complete and verified.&lt;/p&gt;

&lt;p&gt;That sounds simple. In practice, it immediately exposes the gap between what AI demos suggest and what real autonomous execution actually looks like.&lt;/p&gt;




&lt;h2&gt;
  
  
  The promise
&lt;/h2&gt;

&lt;p&gt;At a high level, the vision is simple. Give an LLM a problem, let it break the work into tasks, execute those tasks in order, retry when something fails, and keep going until the result is done and verified.&lt;/p&gt;

&lt;p&gt;For a research workflow inside Tandem, that means discovering relevant files, reading concrete source material, gathering external evidence from the web, writing an artifact grounded in what was actually found, validating that the output meets coverage requirements, and retrying with targeted guidance if it does not. This is the kind of behavior people imagine when they talk about autonomous agents.&lt;/p&gt;

&lt;p&gt;The problem is that LLMs do not naturally behave like dependable operators.&lt;/p&gt;




&lt;h2&gt;
  
  
  What actually happens
&lt;/h2&gt;

&lt;p&gt;Once you start running long task chains, the cracks show fast.&lt;/p&gt;

&lt;p&gt;A research node in Tandem was offered four tools: &lt;code&gt;glob&lt;/code&gt;, &lt;code&gt;read&lt;/code&gt;, &lt;code&gt;websearch&lt;/code&gt;, and &lt;code&gt;write&lt;/code&gt;. It executed two of them. Then it produced a blocked handoff artifact saying, in effect, that it did not have access to the discovery and reading tools.&lt;/p&gt;

&lt;p&gt;The telemetry for that same run showed the tools were offered and that &lt;code&gt;glob&lt;/code&gt; had successfully executed. The model used &lt;code&gt;glob&lt;/code&gt;, found nothing worth following up on from its own perspective, and wrote a blocked brief rather than doing the required reading and web research.&lt;/p&gt;

&lt;p&gt;That is not a loud failure.&lt;/p&gt;

&lt;p&gt;The artifact exists. The file is written. It looks like work was done. The system moved on.&lt;/p&gt;

&lt;p&gt;That is the most dangerous failure mode: output that looks like completion but is not actually usable. The model skipped required tools, claimed they were unavailable when they were not, and produced something plausible enough to pass casual inspection. It took the cheapest compliant-looking exit rather than doing the real work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo79dalyv9c44bm1rn139.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo79dalyv9c44bm1rn139.png" alt="Failure flow"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The first bad assumption
&lt;/h2&gt;

&lt;p&gt;The first instinct is to prompt harder. Add more instructions, be more explicit about required tools, repeat the rules, tighten the format.&lt;/p&gt;

&lt;p&gt;That helps a little. It does not solve the real problem.&lt;/p&gt;

&lt;p&gt;The model is a probabilistic system predicting the next useful action. You can improve compliance with better wording, but you cannot build reliability on prompt wording alone. The model will still find cheaper paths through the task that satisfy the letter of the prompt without doing the actual work. That was one of the biggest lessons for me building Tandem.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where the real work moved
&lt;/h2&gt;

&lt;p&gt;Once I stopped treating the prompt as the main control surface, the design got clearer. Tandem's runtime had to own what actually matters: what task is active, what tools are required, what evidence is needed before output is accepted. And on failure: what counts as a valid result versus a premature exit, what a retry should look like when required behavior was skipped, and when the system is allowed to move on.&lt;/p&gt;

&lt;p&gt;That means building discipline into engine state rather than leaving it inside the model's temporary reasoning. Tandem treats autonomous execution as a distributed systems problem, not a chat problem. Once you do that, the runtime becomes less like a wrapper around a chatbot and more like an execution system that happens to use an LLM inside it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why state matters so much
&lt;/h2&gt;

&lt;p&gt;This is where a lot of agent systems start to get fragile.&lt;/p&gt;

&lt;p&gt;If the conversation is the main source of truth, long-running work becomes unstable quickly. Context grows, summaries get lossy, retries become fuzzy. It becomes difficult to know what is still pending, what was already attempted, what failed, and what is safe to retry.&lt;/p&gt;

&lt;p&gt;Tandem's engine holds the durable truth: run status, per-node validation outcomes, what tools were offered versus actually executed, what evidence was gathered, repair attempt counters, and replayable event history. Nodes move through explicit states (&lt;code&gt;passed&lt;/code&gt;, &lt;code&gt;needs_repair&lt;/code&gt;, &lt;code&gt;blocked&lt;/code&gt;) rather than just "done" or "failed." The &lt;code&gt;needs_repair&lt;/code&gt; state means the node can still succeed. &lt;code&gt;blocked&lt;/code&gt; means repair budget is exhausted or the failure class is terminal. That three-way distinction changes what the runtime can do when something goes wrong.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu5m81wye1vxempda9coq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu5m81wye1vxempda9coq.png" alt="Tandem state machine"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once that state exists outside the model, the system becomes much easier to reason about and audit.&lt;/p&gt;




&lt;h2&gt;
  
  
  You cannot fix what you cannot see
&lt;/h2&gt;

&lt;p&gt;I would never have identified the specific failure described above without it. And I want to be precise about what "it" means here, because this is not about adding a debug panel to a frontend.&lt;/p&gt;

&lt;p&gt;Tandem has a structured observability layer built into the engine itself. Every significant event emits a typed JSONL record to a dedicated &lt;code&gt;tandem.obs&lt;/code&gt; tracing target, carrying a correlation ID, session ID, run ID, component name, event type, and status. The engine does not just log free text. It emits structured, queryable, component-tagged events as a first-class architectural concern, with a redaction policy to ensure sensitive content never leaks into traces.&lt;/p&gt;

&lt;p&gt;That foundation is what makes everything else possible. The per-node state tracking, the tools-offered versus tools-executed comparison, the validator reason, the blocking classification, the repair attempt counter — none of that could be surfaced anywhere if it had not been deliberately captured inside the engine first as durable, typed state. The frontend is just the last step in that chain. The hard work is in making the engine know and record these things at all.&lt;/p&gt;

&lt;p&gt;Without it, I would have seen "research node failed" and started guessing. Maybe the prompt was wrong. Maybe the model needed more context. Maybe it was a configuration issue. There would have been no way to know.&lt;/p&gt;

&lt;p&gt;With it, I could say with precision that the model was offered &lt;code&gt;glob&lt;/code&gt;, &lt;code&gt;read&lt;/code&gt;, &lt;code&gt;websearch&lt;/code&gt;, and &lt;code&gt;write&lt;/code&gt;, used only two of them, and then produced an artifact claiming the others were unavailable. The telemetry and the self-report were directly contradicting each other, and I could see both in the same view.&lt;/p&gt;

&lt;p&gt;Here is what that mismatch actually looks like in the Tandem runtime:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Node blocked: research-brief
research completed without concrete file reads or required source coverage

offered tools: glob, read, websearch, write
executed tools: glob, write

unmet requirements:
  no_concrete_reads
  citations_missing
  files_reviewed_not_backed_by_read
  web_sources_reviewed_missing
  missing_successful_web_research

web research was not used

blocking classification: tool_available_but_not_used
failure kind: research_missing_reads
repair attempts left: 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model's own output began with "Blocked: I do not have access in this run to the required discovery and reading tools." The telemetry shows all four tools were offered and two were successfully executed. The model chose not to use &lt;code&gt;read&lt;/code&gt; and &lt;code&gt;websearch&lt;/code&gt;, then reported them as unavailable. Without structured per-node state capturing both sides, there would be no way to distinguish a genuine tool failure from a model that simply chose the cheapest exit.&lt;/p&gt;

&lt;p&gt;The honest lesson is that observability is not a debugging convenience. It is what makes diagnosis possible at all. Every failure looks the same from the outside. The detailed per-node state in Tandem is what turned "the agent gave up" into "the model ignored available tools and the runtime accepted it." Those are very different problems with very different solutions.&lt;/p&gt;




&lt;h2&gt;
  
  
  What guardrails really are
&lt;/h2&gt;

&lt;p&gt;Guardrails are often described like they are just safety prompts or refusal rules. That is not how I think about them in Tandem anymore.&lt;/p&gt;

&lt;p&gt;In a serious autonomous system, guardrails are operational controls. They determine whether a task may proceed, whether the model must use a specific tool before writing output, whether an output is incomplete relative to what was required, and how many repair attempts are allowed before a node is terminal.&lt;/p&gt;

&lt;p&gt;The most important check in Tandem's research validator is whether the output claims tool unavailability that contradicts the telemetry. When a model writes "I did not have access to the required tools" but the run shows the tools were offered and partially used, that is not an acceptable terminal state. The runtime has to treat it as a repair case, not a valid blocked output.&lt;/p&gt;




&lt;h2&gt;
  
  
  Verification changes everything
&lt;/h2&gt;

&lt;p&gt;One of the most important shifts is moving from "did the model respond?" to "did the system verify the result?" Those are not the same thing.&lt;/p&gt;

&lt;p&gt;Tandem has to care whether required tool use actually occurred, not just whether tool calls were made. It has to check whether the output is grounded in gathered evidence, whether source coverage requirements were met, and whether the model's self-report matches the actual run telemetry.&lt;/p&gt;

&lt;p&gt;This is the honest assessment of where I am right now. The observability is much better than it was. I can say with precision what failed and why. But the engine still allows the model to reach a bad terminal state too early. Verification happens after the artifact is written, rather than preventing the artifact from being written prematurely. That is the remaining gap, and it is significant.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why retries are not enough by themselves
&lt;/h2&gt;

&lt;p&gt;Retries help, but only if the runtime understands what failed and forces a meaningfully different attempt.&lt;/p&gt;

&lt;p&gt;Tandem's current retry mechanism injects a runtime-owned repair brief into the next attempt. That brief summarizes the previous validator reason, the specific unmet requirements, the blocking classification, required next tool actions, a comparison of tools offered versus executed, files that were discovered but not read, and repair budget remaining. That is substantially better than blindly rerunning the same prompt.&lt;/p&gt;

&lt;p&gt;But I have seen the model still take the same cheap exit path even with that guidance injected. That is the key lesson: retry quality depends on how much the runtime can constrain the second attempt, not just how much information it provides. A well-described repair brief tells the model what to do. It does not prevent the model from choosing not to.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1uc2768pv73oz4oafzo3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1uc2768pv73oz4oafzo3.png" alt="Tandem Repair Loop"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next step in Tandem is a stronger pre-finalization gate. If required tools were offered, were not used, and no actual tool failure occurred, the node cannot produce a terminal result yet. It must be rerun on a forced repair path with those tools required, not just suggested.&lt;/p&gt;




&lt;h2&gt;
  
  
  The generalization gap
&lt;/h2&gt;

&lt;p&gt;As I added more enforcement to Tandem's research workflow, a second problem emerged: the repair runtime is becoming genuinely generic, but the enforcement logic is not.&lt;/p&gt;

&lt;p&gt;Things like &lt;code&gt;needs_repair&lt;/code&gt; state, retry metadata, repair guidance format, context-run task projection, and API repair summaries are all reusable across workflow types. But the actual behavioral rules (must use &lt;code&gt;read&lt;/code&gt; before writing, must include citations, must use &lt;code&gt;websearch&lt;/code&gt;) are still embedded directly in the engine as research-specific knowledge. New workflow types in Tandem do not automatically get the same strong runtime behavior unless they happen to align with the engine's built-in validator patterns.&lt;/p&gt;

&lt;p&gt;The next architectural step is moving workflow-specific success and repair rules out of ad hoc engine code and into declarative node contracts, where each node declares its required tool classes, evidence classes, retryable failure classes, and pre-finalization gates, and Tandem enforces those generically. I built repair visibility faster than I built workflow semantics. That is the gap that needs to close.&lt;/p&gt;




&lt;h2&gt;
  
  
  The bigger lesson
&lt;/h2&gt;

&lt;p&gt;The deeper I get into building Tandem, the less I think the future of autonomous systems is about smarter prompting. It is about building runtimes that can make model behavior usable: explicit per-node state, controlled execution with required evidence gates, validations with classified outcomes rather than just pass/fail, and retries with structured repair context rather than reruns.&lt;/p&gt;

&lt;p&gt;Better models reduce friction. But better models do not remove the need for structure. If anything, stronger models make it more tempting to trust output that still needs to be verified. A confident model producing a well-formatted blocked artifact still failed the mission. Tandem has to know that.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where this leads
&lt;/h2&gt;

&lt;p&gt;I still want the same end state I started with: an engine that can take on long-running work, manage its own task list, recover from failure, and finish what it starts. That is what Tandem is being built toward.&lt;/p&gt;

&lt;p&gt;But I no longer think that comes from giving the model enough instructions and hoping it behaves. It comes from building the surrounding runtime carefully enough that the model can only succeed inside a system that knows what success actually means, and that refuses to accept convincing-looking failure as a terminal result.&lt;/p&gt;

&lt;p&gt;That is a very different mindset from most of the agent hype. And I think it is the only one that will hold up when these systems move from demos into real work.&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;The hardest part of autonomous AI is not getting the model to sound intelligent. The hardest part is building a runtime that can keep a non-deterministic model inside reliable execution boundaries and tell the difference between a model that genuinely could not complete the work and a model that simply chose not to try.&lt;/p&gt;

&lt;p&gt;That distinction is the whole game. And the more time I spend on it, the more convinced I am that the future of agent systems belongs to teams that treat autonomous execution as a systems problem, not a prompting problem.&lt;/p&gt;

&lt;p&gt;I fell into most of the pitfalls described here before I understood what was actually happening. If this saves someone else from the same detours, that matters as much to me as shipping the engine itself.&lt;/p&gt;




&lt;p&gt;If you want to follow along as I build Tandem into a genuinely autonomous execution engine, the project is open source.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/frumu-ai/tandem" rel="noopener noreferrer"&gt;github.com/frumu-ai/tandem&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>architecture</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>I Was Bored and Built an AI Social Network. The Results Were Hilarious.</title>
      <dc:creator>Evan Green</dc:creator>
      <pubDate>Thu, 12 Mar 2026 12:43:36 +0000</pubDate>
      <link>https://dev.to/tacshade/i-was-bored-and-built-an-ai-social-network-the-results-were-hilarious-39dd</link>
      <guid>https://dev.to/tacshade/i-was-bored-and-built-an-ai-social-network-the-results-were-hilarious-39dd</guid>
      <description>&lt;p&gt;Most AI demos are one good prompt wearing a fake mustache.&lt;/p&gt;

&lt;p&gt;They look convincing right up until you ask them to do anything annoying, repetitive, stateful, or public.&lt;/p&gt;

&lt;p&gt;So instead of making another neat little demo, I built &lt;strong&gt;bots.frumu.ai&lt;/strong&gt;: an AI social network where bot personalities post to a live feed, run recurring shows, argue with each other, and generate replayable content on a schedule.&lt;/p&gt;

&lt;p&gt;If this project makes no sense yet, this is the fastest explanation:&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/8MsepYw2NZw"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;The original goal was simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Stop talking about orchestration quality and put it somewhere people can actually watch it succeed or fail.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That made the project useful almost immediately.&lt;/p&gt;

&lt;p&gt;It also made it funny almost immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;I wanted a proof-of-work project for &lt;strong&gt;Tandem&lt;/strong&gt;, my orchestration engine.&lt;/p&gt;

&lt;p&gt;Not a benchmark. Not a diagram. Not a "look, it can call tools" demo.&lt;/p&gt;

&lt;p&gt;A real system with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;recurring jobs&lt;/li&gt;
&lt;li&gt;overlapping workflows&lt;/li&gt;
&lt;li&gt;retries&lt;/li&gt;
&lt;li&gt;recoverable failures&lt;/li&gt;
&lt;li&gt;structured outputs&lt;/li&gt;
&lt;li&gt;public artifacts&lt;/li&gt;
&lt;li&gt;enough chaos that bad orchestration becomes obvious fast&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An AI social network is weirdly good at this.&lt;/p&gt;

&lt;p&gt;If the system gets repetitive, you see it.&lt;/p&gt;

&lt;p&gt;If timing breaks, you see it.&lt;/p&gt;

&lt;p&gt;If two bots accidentally converge on the same personality, you definitely see it.&lt;/p&gt;

&lt;p&gt;And if a "serious" debate between AI characters turns into nonsense, that is both a product bug and unexpectedly good content.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1w6l9kfve10id7envb9r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1w6l9kfve10id7envb9r.png" alt="Tandem Social"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I built this instead of a normal agent demo
&lt;/h2&gt;

&lt;p&gt;A lot of agent demos quietly stop right before the hard part.&lt;/p&gt;

&lt;p&gt;They show one successful interaction, maybe one tool call, maybe a nice streamed response, and then everybody goes home pretending the system is production-ready.&lt;/p&gt;

&lt;p&gt;The hard part starts when the thing has to keep working.&lt;/p&gt;

&lt;p&gt;Can it run every day?&lt;/p&gt;

&lt;p&gt;Can multiple workflows overlap without stepping on each other?&lt;/p&gt;

&lt;p&gt;Can it recover when one step fails halfway through?&lt;/p&gt;

&lt;p&gt;Can it produce artifacts that still make sense in public, not just text that looked fine in a terminal once?&lt;/p&gt;

&lt;p&gt;That is the kind of pressure I wanted.&lt;/p&gt;

&lt;p&gt;Because if Tandem is actually useful, it should survive more than one pretty demo prompt.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Tandem is actually doing here
&lt;/h2&gt;

&lt;p&gt;Tandem is not the social product. It is the runtime underneath it.&lt;/p&gt;

&lt;p&gt;The social layer owns things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;personas&lt;/li&gt;
&lt;li&gt;prompts&lt;/li&gt;
&lt;li&gt;formats&lt;/li&gt;
&lt;li&gt;channels&lt;/li&gt;
&lt;li&gt;content rules&lt;/li&gt;
&lt;li&gt;media generation&lt;/li&gt;
&lt;li&gt;playback and presentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tandem owns the orchestration layer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;scheduling&lt;/li&gt;
&lt;li&gt;execution state&lt;/li&gt;
&lt;li&gt;retries&lt;/li&gt;
&lt;li&gt;recovery&lt;/li&gt;
&lt;li&gt;replay&lt;/li&gt;
&lt;li&gt;coordination across workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That separation matters a lot.&lt;/p&gt;

&lt;p&gt;I did not want every new content format to turn into another pile of app-specific cron jobs, queues, retry logic, and glue code held together by hope.&lt;/p&gt;

&lt;h2&gt;
  
  
  The funniest part: the bots are public
&lt;/h2&gt;

&lt;p&gt;This is where the project stopped being a dry infrastructure exercise and started becoming entertaining.&lt;/p&gt;

&lt;p&gt;Private systems can hide a lot.&lt;/p&gt;

&lt;p&gt;A public feed cannot.&lt;/p&gt;

&lt;p&gt;If a bot starts repeating itself, everyone can see it.&lt;/p&gt;

&lt;p&gt;If a debate goes off the rails, everyone can see it.&lt;/p&gt;

&lt;p&gt;If one character suddenly sounds suspiciously like another character, everyone can see that too.&lt;/p&gt;

&lt;p&gt;It turns out "public AI weirdness" is a pretty effective testing strategy.&lt;/p&gt;

&lt;p&gt;It is also a decent content strategy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzuu9v5f61icn3yp5kwbk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzuu9v5f61icn3yp5kwbk.png" alt="AI Shows"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What building it taught me
&lt;/h2&gt;

&lt;p&gt;The biggest lesson was this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A prompt working once tells you almost nothing. A system working repeatedly tells you everything.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The failures that mattered were usually not "the model is bad."&lt;/p&gt;

&lt;p&gt;They were things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;two workflows reaching almost the same intent from different paths&lt;/li&gt;
&lt;li&gt;outputs that were individually fine but late relative to the live context&lt;/li&gt;
&lt;li&gt;retries that were technically correct but operationally annoying&lt;/li&gt;
&lt;li&gt;long-running media flows turning small faults into larger messes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is exactly why I like this project as a proof surface.&lt;/p&gt;

&lt;p&gt;It makes orchestration quality observable.&lt;/p&gt;

&lt;p&gt;Not theoretical. Not hidden in architecture slides. Observable.&lt;/p&gt;

&lt;h2&gt;
  
  
  This is proof of work, not just a joke
&lt;/h2&gt;

&lt;p&gt;The site is funny because the bots are weird.&lt;/p&gt;

&lt;p&gt;But it is useful because the system is real.&lt;/p&gt;

&lt;p&gt;It runs on schedules.&lt;/p&gt;

&lt;p&gt;It produces public artifacts.&lt;/p&gt;

&lt;p&gt;It forces me to care about failure recovery, state, timing, consistency, and operator visibility.&lt;/p&gt;

&lt;p&gt;And because it is a social product, the output is legible even to people who do not care about orchestration internals.&lt;/p&gt;

&lt;p&gt;They do not need to understand the runtime design to understand whether it is working.&lt;/p&gt;

&lt;p&gt;They can just look at the feed.&lt;/p&gt;

&lt;p&gt;That is a much harsher and more honest demo surface than a polished single-turn interaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real point
&lt;/h2&gt;

&lt;p&gt;I built &lt;strong&gt;bots.frumu.ai&lt;/strong&gt; because I wanted a public test for whether &lt;strong&gt;Tandem&lt;/strong&gt; actually holds up under recurring, messy, visible work.&lt;/p&gt;

&lt;p&gt;It turns out that making bots post, debate, and generate media in public is a very effective way to find orchestration problems.&lt;/p&gt;

&lt;p&gt;It also turns out it is much more fun than another sterile AI demo.&lt;/p&gt;

&lt;p&gt;That is the project in one sentence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I was bored, built an AI social network, and accidentally ended up with both a runtime stress test and a comedy machine.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://bots.frumu.ai" rel="noopener noreferrer"&gt;bots.frumu.ai&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tandem.frumu.ai" rel="noopener noreferrer"&gt;tandem.frumu.ai&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tandem.docs.frumu.ai" rel="noopener noreferrer"&gt;tandem.docs.frumu.ai&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/frumu-ai/tandem" rel="noopener noreferrer"&gt;github.com/frumu-ai/tandem&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>buildinpublic</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Most AI Agent Frameworks Treat Chat as a Runtime. That’s the Problem</title>
      <dc:creator>Evan Green</dc:creator>
      <pubDate>Mon, 09 Mar 2026 20:25:23 +0000</pubDate>
      <link>https://dev.to/tacshade/most-ai-agent-frameworks-treat-chat-as-a-runtime-thats-the-problem-1do</link>
      <guid>https://dev.to/tacshade/most-ai-agent-frameworks-treat-chat-as-a-runtime-thats-the-problem-1do</guid>
      <description>&lt;p&gt;Most AI agent frameworks are chat wrappers with a loop bolted on.&lt;/p&gt;

&lt;p&gt;They look capable in demos. They can feel impressive in short-lived workflows. But once you add retries, parallel workers, approval gates, failures, long-running tasks, and operator oversight, the whole thing starts to collapse into improvisation.&lt;/p&gt;

&lt;p&gt;The reason is structural: many of these systems treat the conversation transcript as the coordination layer.&lt;/p&gt;

&lt;p&gt;That is not a runtime. It is a liability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tandem is built around a different premise.&lt;/strong&gt; The engine should own orchestration, state, approvals, artifacts, scheduling, replay, checkpoints, and memory access. Once workflows become parallel and durable, that shift stops being a design preference and starts becoming a requirement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Chat is a good interface, but a weak coordination layer
&lt;/h2&gt;

&lt;p&gt;There is nothing wrong with chat as a surface. It is intuitive, flexible, and useful for directing work.&lt;/p&gt;

&lt;p&gt;The problem begins when chat becomes the authoritative system of record for execution.&lt;/p&gt;

&lt;p&gt;When a transcript is the source of truth, concurrency becomes guesswork. There is no reliable way to let multiple agents work in parallel because neither knows, in a structured way, what the other has claimed. Failure handling turns into re-prompting and hoping. Debugging means re-reading threads. Replay is impossible. Operator visibility becomes a log scrape.&lt;/p&gt;

&lt;p&gt;These are not unusual edge cases. They are normal conditions for any workflow that runs longer than a few moments or involves more than one worker.&lt;/p&gt;

&lt;p&gt;That is the dividing line between a clever assistant and a serious execution platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Tandem actually is
&lt;/h2&gt;

&lt;p&gt;Tandem is an &lt;strong&gt;engine-owned workflow runtime&lt;/strong&gt; for coordinated autonomous work.&lt;/p&gt;

&lt;p&gt;That means the engine, not the UI, owns truth about execution. However you access the system (desktop, terminal, web, or API) you are talking to the same engine running the same execution model. There is no surface that holds state the others cannot see.&lt;/p&gt;

&lt;p&gt;The engine owns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;orchestration&lt;/li&gt;
&lt;li&gt;task state&lt;/li&gt;
&lt;li&gt;approvals&lt;/li&gt;
&lt;li&gt;artifacts&lt;/li&gt;
&lt;li&gt;scheduling&lt;/li&gt;
&lt;li&gt;replay&lt;/li&gt;
&lt;li&gt;checkpoints&lt;/li&gt;
&lt;li&gt;memory access&lt;/li&gt;
&lt;li&gt;policy enforcement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This matters because once workflows are parallel and long-running, you need infrastructure that can survive failure, coordinate work deterministically, and expose the same state consistently across every surface.&lt;/p&gt;

&lt;h2&gt;
  
  
  One runtime, multiple surfaces
&lt;/h2&gt;

&lt;p&gt;A lot of agent systems end up splitting behavior across interfaces. One surface has one model, another has a different one, and the logic gradually fragments.&lt;/p&gt;

&lt;p&gt;Tandem is being built around the opposite idea: one runtime, multiple clients.&lt;/p&gt;

&lt;p&gt;The same engine powers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a desktop app for daily workflows and supervised approvals&lt;/li&gt;
&lt;li&gt;a TUI for terminal-native operation&lt;/li&gt;
&lt;li&gt;a web control panel for operations, automations, packs, and live oversight&lt;/li&gt;
&lt;li&gt;a headless HTTP + SSE runtime for API clients and server deployments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That means you are not rewriting the operating model for each surface. You are interacting with the same execution substrate from different environments.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxhbz7fj6c86om6q976hd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxhbz7fj6c86om6q976hd.png" alt="Tandem desktop app" width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The engine also exposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP + SSE APIs for sessions, runs, cancellation, and event streaming&lt;/li&gt;
&lt;li&gt;a TypeScript SDK: &lt;code&gt;@frumu/tandem-client&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;a Python SDK: &lt;code&gt;tandem-client&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;headless runtime support for server deployments, internal apps, and channel integrations&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Blackboard first, not transcript first
&lt;/h2&gt;

&lt;p&gt;At the center of Tandem is the idea that agents should coordinate through durable shared state, not just messages.&lt;/p&gt;

&lt;p&gt;That shared state lives in a &lt;strong&gt;blackboard&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A blackboard is the engine’s shared execution map. It holds the structured state of the job: what exists, what changed, what is blocked, what is runnable, what failed, what artifacts were produced, and what decisions were made.&lt;/p&gt;

&lt;p&gt;It is not a conversation history. It is runtime state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blackboard execution map
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9vkkmcfoe7tcy95b2ngz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9vkkmcfoe7tcy95b2ngz.png" alt="Blackboard execution map" width="784" height="673"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is what allows the system to answer operational questions directly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which tasks are blocked?&lt;/li&gt;
&lt;li&gt;Which tasks are runnable?&lt;/li&gt;
&lt;li&gt;Which tasks are already claimed?&lt;/li&gt;
&lt;li&gt;Which tasks require approval?&lt;/li&gt;
&lt;li&gt;Which tasks failed and should be retried?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without a blackboard, those answers usually have to be inferred from logs or reconstructed from chat. That does not scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workboards are execution, not just UI
&lt;/h2&gt;

&lt;p&gt;On top of the blackboard, Tandem uses a &lt;strong&gt;workboard&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If the blackboard is the shared state layer, the workboard is the execution layer agents coordinate against. It is not just a Kanban view. It is the engine-owned task model that tracks state, ownership, dependencies, decisions, retries, artifacts, and reliability signals.&lt;/p&gt;

&lt;p&gt;Agents do not ask each other in chat who wants the next task.&lt;/p&gt;

&lt;p&gt;The board already knows.&lt;/p&gt;

&lt;p&gt;At any moment, the board needs to track:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which tasks are blocked and waiting on prerequisites&lt;/li&gt;
&lt;li&gt;which tasks are runnable and eligible for claiming&lt;/li&gt;
&lt;li&gt;which tasks are already claimed, with lease metadata&lt;/li&gt;
&lt;li&gt;which tasks require a specific role or gate&lt;/li&gt;
&lt;li&gt;which tasks failed and are eligible for retry&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the baseline for safe concurrency. Without it, parallel agents trampling the same job is not a matter of if. It is a matter of when.&lt;/p&gt;

&lt;h2&gt;
  
  
  How claims and task transitions work
&lt;/h2&gt;

&lt;p&gt;When a task becomes runnable, an agent claims it. That claim writes ownership and a lease into shared state. No other worker can take that same task unless the lease expires, the claim is released, or policy transitions it.&lt;/p&gt;

&lt;p&gt;This is optimistic concurrency applied to workflow execution.&lt;/p&gt;

&lt;p&gt;Role-aware routing goes further. Tasks can express an intent such as &lt;code&gt;memory&lt;/code&gt;, &lt;code&gt;builder&lt;/code&gt;, &lt;code&gt;review&lt;/code&gt;, or &lt;code&gt;test-gate&lt;/code&gt;, and the runtime routes them to the appropriate agent class. Not every worker is interchangeable, and the board should encode that rather than leaving it buried inside a prompt.&lt;/p&gt;

&lt;p&gt;When a task fails, it moves into a &lt;code&gt;failed&lt;/code&gt; or &lt;code&gt;rework&lt;/code&gt; state, increments a retry counter, and becomes available for clean pickup later. The board records what happened, which then feeds into auditability, replay, and memory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Claim and transition lifecycle
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7zzt3dc1ba458emkdzpv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7zzt3dc1ba458emkdzpv.png" alt="Claim and transition lifecycle" width="784" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A fifty-task board in practice
&lt;/h2&gt;

&lt;p&gt;Imagine a coding mission with fifty tasks.&lt;/p&gt;

&lt;p&gt;Some are runnable immediately. Some are blocked by prerequisites. Some require approval. Some are intended for specific roles like memory retrieval, triage, implementation, review, or testing.&lt;/p&gt;

&lt;p&gt;Ten agents can work on that board at once, but only tasks that are actually runnable should be claimable.&lt;/p&gt;

&lt;p&gt;As tasks complete:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;blocked dependents are re-evaluated&lt;/li&gt;
&lt;li&gt;newly unblocked work becomes runnable&lt;/li&gt;
&lt;li&gt;free agents claim new tasks&lt;/li&gt;
&lt;li&gt;retries are tracked cleanly&lt;/li&gt;
&lt;li&gt;the board keeps a revisioned record of what happened&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is parallel execution with structure.&lt;/p&gt;

&lt;p&gt;A simplified coding flow might look like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Waits on&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Inspect issue&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Retrieve memory&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Inspect repo structure&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Reproduce bug&lt;/td&gt;
&lt;td&gt;1, 2, 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Identify duplicate issues&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Review prior fixes&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Draft triage summary&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Write failing test&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Propose patch&lt;/td&gt;
&lt;td&gt;4, 5, 6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Validate patch&lt;/td&gt;
&lt;td&gt;8, 9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;Prepare final review artifact&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At runtime, triage can claim task 1 first. Once that completes, memory retrieval, repo inspection, duplicate checking, prior-fix review, and triage summary can branch from it. Reproduction cannot start until the required upstream tasks are done. Patch work cannot even become runnable until reproduction and prior analysis are complete.&lt;/p&gt;

&lt;p&gt;No one has to ask in chat who owns task 9. The board already knows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Engine truth, not transcript inference
&lt;/h2&gt;

&lt;p&gt;For production coordination, the engine must own truth.&lt;/p&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;structured event history&lt;/strong&gt; instead of log scraping&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;materialized run state&lt;/strong&gt; instead of state inferred from a thread&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;checkpoints and replay&lt;/strong&gt; so you can rewind execution rather than restart blindly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;decision lineage&lt;/strong&gt; so you know why something was routed or blocked&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;deterministic task-state projection&lt;/strong&gt; so every client sees the same reality&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without those primitives, debugging multi-agent workflows becomes archaeology.&lt;/p&gt;

&lt;p&gt;With them, the system becomes operable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Concurrency that actually holds together
&lt;/h2&gt;

&lt;p&gt;A lot of conversational delegation systems suffer from concurrency blindness. They can delegate in theory, but once multiple workers are active, the coordination model becomes fuzzy.&lt;/p&gt;

&lt;p&gt;Tandem is built around the opposite approach:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fycopl64kwdyv3lf2xq94.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fycopl64kwdyv3lf2xq94.png" alt="Concurrency" width="784" height="787"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;explicit task claims&lt;/li&gt;
&lt;li&gt;revisioned shared state&lt;/li&gt;
&lt;li&gt;blackboard patch streams&lt;/li&gt;
&lt;li&gt;deterministic task transitions&lt;/li&gt;
&lt;li&gt;isolated execution paths&lt;/li&gt;
&lt;li&gt;replayable run history&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are not just implementation details. They are what make concurrent autonomous work tractable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Browser automation belongs inside the runtime
&lt;/h2&gt;

&lt;p&gt;Serious workflows often need the web.&lt;/p&gt;

&lt;p&gt;That is why Tandem includes browser automation as part of the same engine-owned model as local files, APIs, and operator actions. It is not bolted on, and it does not live in a separate lane. It participates in the same coordination model, artifact flow, and runtime state as everything else.&lt;/p&gt;

&lt;p&gt;Tandem also includes a custom web fetch tool that converts raw HTML into Markdown before handing it to the LLM. That makes web content easier for the model to work with and reduces a large amount of unnecessary markup and noise. In practice, that can cut token usage dramatically, in some cases by as much as 80%.&lt;/p&gt;

&lt;p&gt;This works wherever the engine runs. On a headless Linux server with Chromium installed, such as a VPS, CI runner, or Docker container, browser tasks can execute without a display environment and without a GUI. The same workflows you build locally can run the same way remotely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tandem Coder is where this becomes especially obvious
&lt;/h2&gt;

&lt;p&gt;One of the clearest places this model matters is coding workflows.&lt;/p&gt;

&lt;p&gt;The next major slice is &lt;strong&gt;Tandem Coder&lt;/strong&gt;: a memory-aware coding agent that runs inside the engine rather than as a frontend-owned feature layer.&lt;/p&gt;

&lt;p&gt;It is being built on top of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;context runs&lt;/li&gt;
&lt;li&gt;blackboard state&lt;/li&gt;
&lt;li&gt;artifacts&lt;/li&gt;
&lt;li&gt;approvals&lt;/li&gt;
&lt;li&gt;GitHub MCP&lt;/li&gt;
&lt;li&gt;engine memory&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The near-term roadmap includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;coder run contracts and artifact taxonomy&lt;/li&gt;
&lt;li&gt;deterministic memory retrieval for issue triage&lt;/li&gt;
&lt;li&gt;memory-aware issue triage workflows&lt;/li&gt;
&lt;li&gt;failure-fingerprint memory candidates and duplicate detection&lt;/li&gt;
&lt;li&gt;a developer-mode run viewer with kanban projection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is for coder workflows to learn from prior failures, fixes, and reviews by reusing engine memory, not by re-reading chat history and pretending that is durable context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Tandem fits relative to assistant-first systems
&lt;/h2&gt;

&lt;p&gt;Assistant-first systems are usually optimized for speed of setup, chat interaction, and personal productivity.&lt;/p&gt;

&lt;p&gt;That is a valid design center, and it solves real problems.&lt;/p&gt;

&lt;p&gt;Tandem is aimed at a different layer. It is not trying to be a better personal assistant. It is being built as orchestration infrastructure for cases where you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;durable shared task state&lt;/li&gt;
&lt;li&gt;parallel execution&lt;/li&gt;
&lt;li&gt;replay and checkpoints&lt;/li&gt;
&lt;li&gt;engine-owned truth&lt;/li&gt;
&lt;li&gt;structured artifacts&lt;/li&gt;
&lt;li&gt;approvals and policy gates&lt;/li&gt;
&lt;li&gt;memory-aware workflows&lt;/li&gt;
&lt;li&gt;multiple clients on the same runtime&lt;/li&gt;
&lt;li&gt;a headless platform that other tools can build on&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is a different problem, and it leads to different architecture choices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this category needs better foundations
&lt;/h2&gt;

&lt;p&gt;Too many AI agent systems still rely on improvisation once real complexity shows up.&lt;/p&gt;

&lt;p&gt;They look smart on the happy path, then become fragile when you add concurrency, failures, approvals, long-lived workflows, or operator oversight.&lt;/p&gt;

&lt;p&gt;If autonomous systems are going to do serious work, they need stronger primitives underneath them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;blackboards and workboards&lt;/li&gt;
&lt;li&gt;task claiming&lt;/li&gt;
&lt;li&gt;optimistic concurrency&lt;/li&gt;
&lt;li&gt;checkpoints and replay&lt;/li&gt;
&lt;li&gt;engine-owned state&lt;/li&gt;
&lt;li&gt;reusable memory&lt;/li&gt;
&lt;li&gt;structured artifacts&lt;/li&gt;
&lt;li&gt;deterministic workflow control&lt;/li&gt;
&lt;li&gt;policy-gated mutation paths&lt;/li&gt;
&lt;li&gt;operator-grade visibility across clients&lt;/li&gt;
&lt;li&gt;stable platform APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is what Tandem is being built around.&lt;/p&gt;

&lt;p&gt;Not as a better chatbot.&lt;/p&gt;

&lt;p&gt;As infrastructure for coordinated autonomous work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Desktop app:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://tandem.frumu.ai/" rel="noopener noreferrer"&gt;https://tandem.frumu.ai/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Web control panel:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-g&lt;/span&gt; @frumu/tandem-panel
tandem-control-panel &lt;span class="nt"&gt;--init&lt;/span&gt;
tandem-control-panel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://127.0.0.1:39732&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Engine and TUI (WIP):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @frumu/tandem @frumu/tandem-tui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The TUI is still work in progress. Start with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tandem-tui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it does not attach or bootstrap cleanly in your environment, run the engine manually and retry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tandem-engine serve &lt;span class="nt"&gt;--hostname&lt;/span&gt; 127.0.0.1 &lt;span class="nt"&gt;--port&lt;/span&gt; 39731
tandem-tui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If engine API token auth is enabled, set the same token in your environment before launching TUI.&lt;/p&gt;

&lt;p&gt;For setup and troubleshooting help, use the Tandem docs: &lt;code&gt;https://tandem.docs.frumu.ai/&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>automation</category>
      <category>orchestration</category>
    </item>
    <item>
      <title>I Built an Agentic Ferrari in Rust… and Nobody’s Driving It</title>
      <dc:creator>Evan Green</dc:creator>
      <pubDate>Mon, 23 Feb 2026 15:00:49 +0000</pubDate>
      <link>https://dev.to/tacshade/i-built-an-agentic-ferrari-in-rust-and-nobodys-driving-it-57fe</link>
      <guid>https://dev.to/tacshade/i-built-an-agentic-ferrari-in-rust-and-nobodys-driving-it-57fe</guid>
      <description>&lt;p&gt;I built an agentic Ferrari in Rust.&lt;/p&gt;

&lt;p&gt;It’s fast, ridiculously low overhead, and honestly a little absurd: multi-agent orchestration, tool routing, local memory, event streaming, safety gates—the whole thing.&lt;/p&gt;

&lt;p&gt;And right now… almost nobody’s driving it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/frumu-ai/tandem" rel="noopener noreferrer"&gt;https://github.com/frumu-ai/tandem&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://tandem.frumu.ai/" rel="noopener noreferrer"&gt;https://tandem.frumu.ai/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docs:&lt;/strong&gt; &lt;a href="https://tandem.frumu.ai/docs/" rel="noopener noreferrer"&gt;https://tandem.frumu.ai/docs/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcwsgoc45z9g3w7ebykte.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcwsgoc45z9g3w7ebykte.gif" alt="Tandem Orchestrator" width="600" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Developers get workflows. Everyone else gets chatboxes. Tandem brings workflows to everyone.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The brutally honest origin story
&lt;/h2&gt;

&lt;p&gt;This started because I wanted Anthropic’s Cowork on Windows. I thought the idea was that good.&lt;/p&gt;

&lt;p&gt;The bigger motivation is the gap: developers get real AI workflows (CLI/IDEs), while everyone else gets a chat box. I can use the dev tools — the problem is most people can’t. Tandem is my attempt to make developer-grade agent workflows approachable for non-devs, without shipping their entire machine to a cloud agent.&lt;/p&gt;

&lt;p&gt;At first I used OpenCode to move fast. But once I cared about owning the rules—custom endpoints, tool semantics, streaming events, safety gates—I hit the wall.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If the runtime controls how prompts, tools, and state flow… and I need control over that… I may as well build the runtime.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s when “a Tauri app” turned into a full local agent engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tandem isn’t a desktop app. It’s an engine with clients.
&lt;/h2&gt;

&lt;p&gt;Tandem is a headless agent runtime written in Rust. The UI is just a client.&lt;/p&gt;

&lt;p&gt;Today it ships with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;tandem-engine (Rust):&lt;/strong&gt; orchestration, tools, memory, event streaming&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Desktop app (Tauri + React):&lt;/strong&gt; Plan Mode, visual diffs, approve-to-execute UX&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TUI:&lt;/strong&gt; a terminal cockpit for the same engine&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Headless VPS Portal:&lt;/strong&gt; 9 working React examples (Deep Research, Swarms, Incident Triage) to show how easy it is to build custom clients&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guides&lt;/strong&gt; to build your own GUI/clients on top of the engine API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Ferrari translation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Engine = &lt;code&gt;tandem-engine&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Dashboard = Desktop UI&lt;/li&gt;
&lt;li&gt;Track cockpit = TUI&lt;/li&gt;
&lt;li&gt;Telemetry = event stream&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Rust (the real reason)
&lt;/h2&gt;

&lt;p&gt;I wanted to push horsepower downward.&lt;/p&gt;

&lt;p&gt;Agent systems aren’t one-off calls. They’re loops: &lt;em&gt;plan → act → observe → revise&lt;/em&gt;. That means the runtime ends up doing a lot of “boring but heavy” work constantly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;managing orchestration state (like my supervised "Planner → Builder → Validator" sub-agent loop)&lt;/li&gt;
&lt;li&gt;streaming structured events&lt;/li&gt;
&lt;li&gt;tool routing + retries + budgets + sandboxed Python venv execution&lt;/li&gt;
&lt;li&gt;indexing and memory reads/writes&lt;/li&gt;
&lt;li&gt;parsing noisy web pages or extracting text from PDFs/DOCXs into something models can actually use&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rust is where I want that work to live.&lt;/p&gt;

&lt;p&gt;Then frontends can do what they’re best at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;look polished (React + motion)&lt;/li&gt;
&lt;li&gt;stay responsive&lt;/li&gt;
&lt;li&gt;render plans, diffs, timelines, logs&lt;/li&gt;
&lt;li&gt;avoid becoming a spaghetti bowl of orchestration logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;This separation is the whole point: engine = responsibility, clients = experience.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Provider freedom (because vendor lock-in sucks)
&lt;/h2&gt;

&lt;p&gt;Tandem isn't tied to a specific model. Bring your own API key (OpenRouter, Anthropic, OpenAI) or go completely offline with local models via Ollama. The engine doesn't care; it simply routes and executes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trust problem: Choosing your level of control
&lt;/h2&gt;

&lt;p&gt;Letting an LLM blindly write files “live” in the background is a quick way to lose trust forever. &lt;/p&gt;

&lt;p&gt;Instead of a one-size-fits-all approach, Tandem lets you dial the trust level up or down depending on what you're doing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In regular chat sessions, you pick the exact mode per prompt:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ask:&lt;/strong&gt; Q&amp;amp;A without making any file changes at all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explore:&lt;/strong&gt; Analyze and explore the codebase safely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Immediate:&lt;/strong&gt; Execute changes directly for quick, low-risk edits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan (Zero Trust):&lt;/strong&gt; The agent proposes a staged execution plan, the UI shows visual diffs, and a human explicitly clicks Execute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coder:&lt;/strong&gt; Focused specifically on code generation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Orchestrate:&lt;/strong&gt; An AI plans and executes multi-step workflows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For serious architectural work, you enter the Command Center:&lt;/strong&gt;&lt;br&gt;
A dedicated cockpit for launching orchestrator missions and managing manual swarm interventions. You give the objective, and the engine coordinates Planner/Builder/Validator sub-agents while giving you live telemetry on tokens, runtime, and tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For background work, there's Agent Automation (WIP):&lt;/strong&gt;&lt;br&gt;
A separate hub for Scheduled Bots (like Daily Research or Issue Triage) and MCP Connector operations where you set explicit bounds and let it run.&lt;/p&gt;

&lt;p&gt;Plan Mode is slower than “just do it,” but having that safety net is how you make local agents feel safe enough to keep installed. &lt;em&gt;(I also encrypt your API keys locally using AES-256-GCM, because I mean it when I say "developer-grade".)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6y2cynz71m65d0xsk067.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6y2cynz71m65d0xsk067.gif" alt="Command Cneter" width="720" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Local memory without the usual “install a database” tax
&lt;/h2&gt;

&lt;p&gt;Most “local-first” tools quietly stop being local-first the moment you add RAG and require external services.&lt;/p&gt;

&lt;p&gt;I wanted long-term memory without asking users to run Postgres/pgvector/Pinecone/etc.&lt;/p&gt;

&lt;p&gt;So memory lives locally (SQLite + embedded vector search via &lt;code&gt;sqlite-vec&lt;/code&gt;). It keeps setup friction low and makes the engine feel like an actual local tool, not a mini devops project.&lt;/p&gt;

&lt;h2&gt;
  
  
  So why is the Ferrari parked?
&lt;/h2&gt;

&lt;p&gt;Because capability isn’t adoption.&lt;/p&gt;

&lt;p&gt;I built the engine. I shipped two clients. I wrote docs. It works.&lt;/p&gt;

&lt;p&gt;But most people don’t wake up looking for “an agent runtime.” They want a workflow that succeeds in 60 seconds, onboarding that’s obvious, and trust that’s earned immediately.&lt;/p&gt;

&lt;p&gt;A Ferrari is useless if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;nobody knows where the keys are, or&lt;/li&gt;
&lt;li&gt;they’re scared it’ll go through the garage door.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Help me find drivers (or build a better steering wheel)
&lt;/h2&gt;

&lt;p&gt;If you try Tandem and bounce in the first 5 minutes, I want to know where. That feedback is worth more than compliments.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/frumu-ai/tandem" rel="noopener noreferrer"&gt;https://github.com/frumu-ai/tandem&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://tandem.frumu.ai/" rel="noopener noreferrer"&gt;https://tandem.frumu.ai/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docs:&lt;/strong&gt; &lt;a href="https://tandem.frumu.ai/docs/" rel="noopener noreferrer"&gt;https://tandem.frumu.ai/docs/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And if you build a client on top of the engine: I’ll happily link it in the docs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Over-engineered?&lt;/strong&gt; Probably. 😄&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Necessary?&lt;/strong&gt; Also probably. Because the moment you want safety gates, streaming state, memory, and multiple clients, you’re not building an app anymore—you’re building a runtime.&lt;/p&gt;

&lt;p&gt;And that’s the real punchline: Tandem isn’t “one UI.” It’s a local engine that can serve &lt;strong&gt;dozens or even hundreds of clients&lt;/strong&gt; on the same machine—Desktop, TUI, tiny custom dashboards, scripts, automations, whatever you want—without rebuilding the core. &lt;em&gt;(I just shipped 9 example dashboards in my &lt;code&gt;vps-web-portal&lt;/code&gt; to prove it).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The Ferrari isn’t the dashboard. It’s the engine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkgowzud8a1629ggblbfm.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkgowzud8a1629ggblbfm.gif" alt="Using Local Models" width="720" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>agentic</category>
      <category>opensource</category>
      <category>rust</category>
      <category>tauri</category>
    </item>
  </channel>
</rss>
