Every workflow builder I have used opens the same way: a blank canvas and a
palette of nodes. Zapier, n8n, Make - all of them assume you already know what
you want, already decomposed into steps, before the tool is any use to you.
Most people don't. They know the chore. "I keep forgetting to check the
weather before I bike in." The gap between knowing the chore and knowing the
DAG is precisely the work these tools leave you to do alone, and I think it is
why most people who try one never build a second automation.
So I built Weaver, which inverts it. Weaver interviews you about the chore, one
question at a time, until it actually understands the goal. Then it designs the
workflow, validates it, deploys it, and runs it. The canvas is an output rather
than an input.
This post is about the parts that did not go to plan, because those are the
parts worth reading.
The interview is the whole product
Three rules, and they are harder than they look:
- One question per turn. Never three bundled into a paragraph.
- Never invent a value the person has not given you. No quietly assumed recipient, city, or time.
- A correction updates one detail. Say "actually, Mondays" halfway through and it changes that and keeps going, instead of restarting the interview.
That third one is the one people notice. Restarting an interview because the
user corrected themselves is the single fastest way to make software feel like
it is not listening.
Only once it restates the whole task in plain language and you confirm does it
save the intent and hand off to a separate Designer Agent.
Two agents, deliberately not one
The Conversation Agent and the Designer Agent are different models with
different prompts and no shared state beyond a saved intent.
That is a design decision, not an accident of implementation. Understanding a
person and designing a system are different skills with different failure
modes. Collapsing them into one prompt makes both worse: the interviewer starts
proposing architecture halfway through the conversation, and the designer
starts second-guessing requirements it should be treating as settled.
Between them sits a validator with no model in it at all. Unknown step
types, missing parameters, dangling references, dependency cycles - all caught
by ordinary deterministic code. Asking a model to check its own work is not
validation, it is a second opinion from the same source.
Now the parts that broke
Gemini 3.x is not served on regional Vertex endpoints. Every 3.x model
404'd at us-central1 while gemini-2.5-flash answered fine at the same
address. That reads exactly like a permissions problem, and I treated it as one
for an hour. They need location=global, separate from wherever your Cloud Run
services live.
My workflow engine lied about success. Step agents catch their own tool
errors and return a descriptive string, so the model then paraphrases the
failure into prose that no longer carries an error prefix. Which meant an HTTP
200 told me nothing at all: a step that had completely failed to send an email
reported success, and everything downstream ran on a lie.
The fix was to stop reading the model's final text and start reading the raw
functionResponse in the event stream. A workflow engine that lies about
success is worse than one that crashes, because you find out days later.
I sent someone an email whose entire body was {{compose_briefing.output}}.
The template resolver knew one placeholder spelling; the Designer had emitted a
different one. Substitution silently did nothing and the literal braces
travelled all the way to Gmail.
It now understands four spellings, but the real fix is the guard: any step
whose parameters are still unresolved at dispatch refuses to run. An honest
failure beats a delivered placeholder, every time.
Cloud Scheduler failed with status.code: 7 and a 403 in the Cloud Run
logs. Which looks precisely like a missing run.invoker binding, and is not.
Scheduler must also be able to impersonate the service account to mint the
OIDC token - roles/iam.serviceAccountTokenCreator, a completely separate
grant that nothing in the error surface hints at. Allow two minutes for it to
propagate before deciding it did not work.
Imagen died ten days before my deadline. I built an image-generation step,
deployed it, and got a 404 saying "not found or your project does not have
access" - so naturally I went looking at IAM. It was neither. Every Imagen
endpoint was deprecated on Vertex in March 2026 and shut down on
17 August 2026. The model was simply gone.
I want to flag the thing that actually cost me the time here, because it was my
own mistake rather than Google's. My first availability check sent an empty
request body and got back 400 Empty instances, which I read as "the model
exists, my body was just wrong." It does not mean that. That error fires during
request validation, before the model is resolved, so it returns identically
for models that exist and models that do not. I had built a test that could
only ever pass.
The replacement check sends a real request and reads the status code. It found
in thirty seconds what I had been wrong about for an hour: Veo and Lyria work,
Imagen and every Gemini image model do not.
The bit I am actually proud of
Weaver's capabilities live in a Firestore collection, not in a prompt. The
Designer Agent reads that registry live and is told to refuse anything it
cannot build from what is in there.
Two consequences fell out of that, and I did not plan either.
The first: when I added video and music generation, I added a database row and
a single-purpose agent each. The Designer picked both up with no prompt
change. It designed a four-step workflow - fetch the top tech headline, write
a one-sentence description, generate a video clip from it, post the clip to
Discord - entirely from a plain-language description of what I wanted.
The second: it tells me what it is missing. Early on I asked for something
it could not do, and instead of faking a workflow or apologising generically,
it named the specific gaps: no delay capability, no timezone capability, no way
to count runs and stop after three. All three of those features exist now
because the tool told me, in terms specific enough to act on, what it lacked.
Being able to articulate which capability is missing is the difference
between a collaborator and a broken button.
One design constraint worth stealing
Vertex's media models return bytes. Weaver's steps pass text to one another -
a later step's parameters reference {{step_id.output}}.
Rather than thread binary data between agents, media steps write the file to
Cloud Storage and return a URL. Which meant the Slack and Discord steps
needed zero changes to deliver video and music, because both render a media
URL inline.
The general shape: when a new data type does not fit your pipeline, look for
the representation that turns it back into the type you already move around.
Usually there is one, and it is usually a URL.
What it is made of
Fifteen ADK agents on Cloud Run, every one private, invoked with a per-service
identity token audienced to that exact URL. Gemini 3.7 Flash for the interview,
3.5 Flash for design, 3.5 Flash Lite for the bounded steps, plus Veo 3.1 and
Lyria for media. Firestore for the registry, intents and run history. Secret
Manager for credentials, fetched at the moment of use. Cloud Scheduler so the
schedules survive my laptop being shut.
The lesson, if there is one
Verify against installed reality, not against what you remember the API being.
Almost every hour I lost on this build went to a plausible assumption that a
thirty-second check would have killed - and at least once, to a check I had
written badly enough that it could only confirm what I already believed.
The other one: a run succeeding is not the same as a run being useful. Weaver
asks, after a run, whether the result was any good - a different question from
whether it worked, and the only one the person can answer. That single question
changed the product more than any model choice did.
I built Weaver and wrote this post as my entry to the All Things Agentic
Hackathon.
Weaver is live at https://weaver.bhalla.info - behind an access code while
judging is under way, because the agents send real email and post to real
channels on a real billing account. I will open it up afterwards.
Top comments (2)
The strongest idea here is the model-free validator sitting between agents. I’d take it further: validation shouldn’t only check whether a workflow is structurally valid; it should enforce dispatch-time invariants. A workflow can be perfectly valid at design time and still become invalid when a referenced value, credential, capability, or external API changes.
The {{...}} incident is a good example. Template resolution, tool responses, and model output should have explicit typed contracts, with unresolved values treated as a hard failure not strings that can accidentally make it downstream. That same principle applies to agent success: never infer execution state from the model’s prose when the runtime already has authoritative function/tool events. Let models design and reason; let deterministic infrastructure decide whether something is actually safe and executable.
You already have a strong separation: the Conversation Agent captures intent, the Designer consumes the saved intent, and deterministic code validates the structure. The traceability question starts when the user corrects an accepted intent after workflows already exist.
In my projects, I keep explicit links from the user scenario to acceptance criteria, tests, and implementation decisions. A changed requirement then produces an impact set, not only an updated sentence.
Does Weaver retain those dependencies? If a user changes “send every morning” to “send only on workdays,” can it identify which steps and validations need re-approval before the next run?