<?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: Aaryan Ved Bhalla</title>
    <description>The latest articles on DEV Community by Aaryan Ved Bhalla (@aaryanved).</description>
    <link>https://dev.to/aaryanved</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4097043%2Ff358a829-2c58-430f-bb36-2a05ebc5e654.png</url>
      <title>DEV Community: Aaryan Ved Bhalla</title>
      <link>https://dev.to/aaryanved</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aaryanved"/>
    <language>en</language>
    <item>
      <title>I built a workflow builder that interviews you. Here is what broke.</title>
      <dc:creator>Aaryan Ved Bhalla</dc:creator>
      <pubDate>Thu, 27 Aug 2026 09:36:05 +0000</pubDate>
      <link>https://dev.to/aaryanved/i-built-a-workflow-builder-that-interviews-you-here-is-what-broke-54f</link>
      <guid>https://dev.to/aaryanved/i-built-a-workflow-builder-that-interviews-you-here-is-what-broke-54f</guid>
      <description>&lt;p&gt;Every workflow builder I have used opens the same way: a blank canvas and a&lt;br&gt;
palette of nodes. Zapier, n8n, Make - all of them assume you already know what&lt;br&gt;
you want, already decomposed into steps, before the tool is any use to you.&lt;/p&gt;

&lt;p&gt;Most people don't. They know the &lt;em&gt;chore&lt;/em&gt;. "I keep forgetting to check the&lt;br&gt;
weather before I bike in." The gap between knowing the chore and knowing the&lt;br&gt;
DAG is precisely the work these tools leave you to do alone, and I think it is&lt;br&gt;
why most people who try one never build a second automation.&lt;/p&gt;

&lt;p&gt;So I built Weaver, which inverts it. Weaver interviews you about the chore, one&lt;br&gt;
question at a time, until it actually understands the goal. Then it designs the&lt;br&gt;
workflow, validates it, deploys it, and runs it. The canvas is an output rather&lt;br&gt;
than an input.&lt;/p&gt;

&lt;p&gt;This post is about the parts that did not go to plan, because those are the&lt;br&gt;
parts worth reading.&lt;/p&gt;

&lt;h3&gt;
  
  
  The interview is the whole product
&lt;/h3&gt;

&lt;p&gt;Three rules, and they are harder than they look:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One question per turn.&lt;/strong&gt; Never three bundled into a paragraph.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never invent a value the person has not given you.&lt;/strong&gt; No quietly assumed
recipient, city, or time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A correction updates one detail.&lt;/strong&gt; Say "actually, Mondays" halfway through
and it changes that and keeps going, instead of restarting the interview.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That third one is the one people notice. Restarting an interview because the&lt;br&gt;
user corrected themselves is the single fastest way to make software feel like&lt;br&gt;
it is not listening.&lt;/p&gt;

&lt;p&gt;Only once it restates the whole task in plain language and you confirm does it&lt;br&gt;
save the intent and hand off to a &lt;strong&gt;separate&lt;/strong&gt; Designer Agent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Two agents, deliberately not one
&lt;/h3&gt;

&lt;p&gt;The Conversation Agent and the Designer Agent are different models with&lt;br&gt;
different prompts and no shared state beyond a saved intent.&lt;/p&gt;

&lt;p&gt;That is a design decision, not an accident of implementation. Understanding a&lt;br&gt;
person and designing a system are different skills with different failure&lt;br&gt;
modes. Collapsing them into one prompt makes both worse: the interviewer starts&lt;br&gt;
proposing architecture halfway through the conversation, and the designer&lt;br&gt;
starts second-guessing requirements it should be treating as settled.&lt;/p&gt;

&lt;p&gt;Between them sits a validator with &lt;strong&gt;no model in it at all&lt;/strong&gt;. Unknown step&lt;br&gt;
types, missing parameters, dangling references, dependency cycles - all caught&lt;br&gt;
by ordinary deterministic code. Asking a model to check its own work is not&lt;br&gt;
validation, it is a second opinion from the same source.&lt;/p&gt;

&lt;h3&gt;
  
  
  Now the parts that broke
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Gemini 3.x is not served on regional Vertex endpoints.&lt;/strong&gt; Every 3.x model&lt;br&gt;
404'd at &lt;code&gt;us-central1&lt;/code&gt; while &lt;code&gt;gemini-2.5-flash&lt;/code&gt; answered fine at the same&lt;br&gt;
address. That reads exactly like a permissions problem, and I treated it as one&lt;br&gt;
for an hour. They need &lt;code&gt;location=global&lt;/code&gt;, separate from wherever your Cloud Run&lt;br&gt;
services live.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My workflow engine lied about success.&lt;/strong&gt; Step agents catch their own tool&lt;br&gt;
errors and return a descriptive string, so the model then paraphrases the&lt;br&gt;
failure into prose that no longer carries an error prefix. Which meant an HTTP&lt;br&gt;
200 told me nothing at all: a step that had completely failed to send an email&lt;br&gt;
reported &lt;code&gt;success&lt;/code&gt;, and everything downstream ran on a lie.&lt;/p&gt;

&lt;p&gt;The fix was to stop reading the model's final text and start reading the raw&lt;br&gt;
&lt;code&gt;functionResponse&lt;/code&gt; in the event stream. A workflow engine that lies about&lt;br&gt;
success is worse than one that crashes, because you find out days later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I sent someone an email whose entire body was &lt;code&gt;{{compose_briefing.output}}&lt;/code&gt;.&lt;/strong&gt;&lt;br&gt;
The template resolver knew one placeholder spelling; the Designer had emitted a&lt;br&gt;
different one. Substitution silently did nothing and the literal braces&lt;br&gt;
travelled all the way to Gmail.&lt;/p&gt;

&lt;p&gt;It now understands four spellings, but the real fix is the guard: any step&lt;br&gt;
whose parameters are still unresolved at dispatch &lt;strong&gt;refuses to run&lt;/strong&gt;. An honest&lt;br&gt;
failure beats a delivered placeholder, every time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cloud Scheduler failed with &lt;code&gt;status.code: 7&lt;/code&gt; and a 403 in the Cloud Run&lt;br&gt;
logs.&lt;/strong&gt; Which looks precisely like a missing &lt;code&gt;run.invoker&lt;/code&gt; binding, and is not.&lt;br&gt;
Scheduler must &lt;em&gt;also&lt;/em&gt; be able to impersonate the service account to mint the&lt;br&gt;
OIDC token - &lt;code&gt;roles/iam.serviceAccountTokenCreator&lt;/code&gt;, a completely separate&lt;br&gt;
grant that nothing in the error surface hints at. Allow two minutes for it to&lt;br&gt;
propagate before deciding it did not work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Imagen died ten days before my deadline.&lt;/strong&gt; I built an image-generation step,&lt;br&gt;
deployed it, and got a 404 saying &lt;em&gt;"not found or your project does not have&lt;br&gt;
access"&lt;/em&gt; - so naturally I went looking at IAM. It was neither. Every Imagen&lt;br&gt;
endpoint was deprecated on Vertex in March 2026 and shut down on&lt;br&gt;
17 August 2026. The model was simply gone.&lt;/p&gt;

&lt;p&gt;I want to flag the thing that actually cost me the time here, because it was my&lt;br&gt;
own mistake rather than Google's. My first availability check sent an empty&lt;br&gt;
request body and got back &lt;code&gt;400 Empty instances&lt;/code&gt;, which I read as "the model&lt;br&gt;
exists, my body was just wrong." It does not mean that. That error fires during&lt;br&gt;
request validation, &lt;em&gt;before&lt;/em&gt; the model is resolved, so it returns identically&lt;br&gt;
for models that exist and models that do not. I had built a test that could&lt;br&gt;
only ever pass.&lt;/p&gt;

&lt;p&gt;The replacement check sends a real request and reads the status code. It found&lt;br&gt;
in thirty seconds what I had been wrong about for an hour: Veo and Lyria work,&lt;br&gt;
Imagen and every Gemini image model do not.&lt;/p&gt;

&lt;h3&gt;
  
  
  The bit I am actually proud of
&lt;/h3&gt;

&lt;p&gt;Weaver's capabilities live in a Firestore collection, not in a prompt. The&lt;br&gt;
Designer Agent reads that registry live and is told to refuse anything it&lt;br&gt;
cannot build from what is in there.&lt;/p&gt;

&lt;p&gt;Two consequences fell out of that, and I did not plan either.&lt;/p&gt;

&lt;p&gt;The first: when I added video and music generation, I added a database row and&lt;br&gt;
a single-purpose agent each. &lt;strong&gt;The Designer picked both up with no prompt&lt;br&gt;
change.&lt;/strong&gt; It designed a four-step workflow - fetch the top tech headline, write&lt;br&gt;
a one-sentence description, generate a video clip from it, post the clip to&lt;br&gt;
Discord - entirely from a plain-language description of what I wanted.&lt;/p&gt;

&lt;p&gt;The second: &lt;strong&gt;it tells me what it is missing.&lt;/strong&gt; Early on I asked for something&lt;br&gt;
it could not do, and instead of faking a workflow or apologising generically,&lt;br&gt;
it named the specific gaps: no delay capability, no timezone capability, no way&lt;br&gt;
to count runs and stop after three. All three of those features exist now&lt;br&gt;
because the tool told me, in terms specific enough to act on, what it lacked.&lt;/p&gt;

&lt;p&gt;Being able to articulate &lt;em&gt;which&lt;/em&gt; capability is missing is the difference&lt;br&gt;
between a collaborator and a broken button.&lt;/p&gt;

&lt;h3&gt;
  
  
  One design constraint worth stealing
&lt;/h3&gt;

&lt;p&gt;Vertex's media models return bytes. Weaver's steps pass text to one another -&lt;br&gt;
a later step's parameters reference &lt;code&gt;{{step_id.output}}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Rather than thread binary data between agents, media steps write the file to&lt;br&gt;
Cloud Storage and return a &lt;strong&gt;URL&lt;/strong&gt;. Which meant the Slack and Discord steps&lt;br&gt;
needed &lt;em&gt;zero changes&lt;/em&gt; to deliver video and music, because both render a media&lt;br&gt;
URL inline.&lt;/p&gt;

&lt;p&gt;The general shape: when a new data type does not fit your pipeline, look for&lt;br&gt;
the representation that turns it back into the type you already move around.&lt;br&gt;
Usually there is one, and it is usually a URL.&lt;/p&gt;

&lt;h3&gt;
  
  
  What it is made of
&lt;/h3&gt;

&lt;p&gt;Fifteen ADK agents on Cloud Run, every one private, invoked with a per-service&lt;br&gt;
identity token audienced to that exact URL. Gemini 3.7 Flash for the interview,&lt;br&gt;
3.5 Flash for design, 3.5 Flash Lite for the bounded steps, plus Veo 3.1 and&lt;br&gt;
Lyria for media. Firestore for the registry, intents and run history. Secret&lt;br&gt;
Manager for credentials, fetched at the moment of use. Cloud Scheduler so the&lt;br&gt;
schedules survive my laptop being shut.&lt;/p&gt;

&lt;h3&gt;
  
  
  The lesson, if there is one
&lt;/h3&gt;

&lt;p&gt;Verify against installed reality, not against what you remember the API being.&lt;br&gt;
Almost every hour I lost on this build went to a plausible assumption that a&lt;br&gt;
thirty-second check would have killed - and at least once, to a check I had&lt;br&gt;
written badly enough that it could only confirm what I already believed.&lt;/p&gt;

&lt;p&gt;The other one: a run succeeding is not the same as a run being useful. Weaver&lt;br&gt;
asks, after a run, whether the result was any good - a different question from&lt;br&gt;
whether it worked, and the only one the person can answer. That single question&lt;br&gt;
changed the product more than any model choice did.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I built Weaver and wrote this post as my entry to the All Things Agentic&lt;br&gt;
Hackathon.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Weaver is live at &lt;strong&gt;&lt;a href="https://weaver.bhalla.info" rel="noopener noreferrer"&gt;https://weaver.bhalla.info&lt;/a&gt;&lt;/strong&gt; - behind an access code while&lt;br&gt;
judging is under way, because the agents send real email and post to real&lt;br&gt;
channels on a real billing account. I will open it up afterwards.&lt;/p&gt;

</description>
      <category>googlecloud</category>
      <category>ai</category>
      <category>agents</category>
      <category>python</category>
    </item>
  </channel>
</rss>
