DEV Community

StareBrain
StareBrain

Posted on

The Confirmation Gap: Why "Request Accepted" Isn't "Action Happened"

This week, three unrelated conversations converged on the same unsolved problem, independently. Worth writing down properly instead of three separate comment threads.

Three shapes, one gap

Webhook-triggered side effects. A SaaS production-readiness checklist I looked at covers Stripe webhook authenticity and idempotency — verifying the webhook is real, and that a duplicate delivery doesn't trigger the action twice. Neither covers this: the webhook arrives, you attempt the downstream action it triggers, and that attempt times out or fails silently. The webhook did its job. Your response to it didn't complete, and you don't know if it partially did.

Operations monitoring. A commenter building an ops-monitoring tool (OpsWatch) described handling this as a first-class state: once a dispatch has occurred, if the consequence can't be established, that ambiguity gets preserved as its own status — not silently converted into success, failure, or an automatic retry. The framing that stuck with me: a second attempt, once the first attempt may have already produced the consequence, isn't recovery logic anymore. It's a new consequential action that needs its own authorization.

Agent-dispatched actions. This is StareBrain's version: confirm before execute, dispatch, and then — for some fraction of actions — the result comes back ambiguous. Not "failed" (clean, you know to retry or alert). Not "succeeded" (clean, you're done). Silence, or a malformed response, or a timeout with no way to distinguish "never ran" from "ran and the ack got lost."

Why idempotency doesn't cover this

The instinct is: "isn't this what idempotency keys are for?" No — idempotency solves a different, related problem. An idempotency key protects you from your own retry causing a duplicate effect. It assumes you've already decided to retry. It says nothing about whether you should, because it doesn't tell you whether the first attempt landed.

Put differently: idempotency makes retrying safe once you've decided to retry. It doesn't help you decide whether retrying is the right move at all. Those are separate problems, and most systems only build for the first one because it's the one with an established pattern (idempotency keys, request IDs) and a name you can put in a checklist.

The second problem doesn't have an established pattern yet, at least not one I've found in production use. It doesn't even have a settled name. "Outcome-unknown state," "unresolved consequence," "ambiguous dispatch" — different people describing it are reaching for different words, which is itself a signal that nobody's converged on the right abstraction.

What a real answer probably needs

Based on what came out of these conversations, a genuine solution needs at least three properties an idempotency key alone doesn't give you:

A distinguishable third state. Not success, not failure — a state that means "we don't know," that downstream logic can check for explicitly rather than defaulting to one of the other two. This sounds trivial to say and isn't trivial to build, because most state machines are designed around binary or enumerable outcomes, not "unknown, pending resolution."
A rule for what retry means once you're in that state. If the first attempt might have landed, a retry is a new action, not a repair of the old one — which means it needs its own authorization, not an automatic retry policy. This is the part that actually differs from normal error handling: normal errors assume "didn't happen," so retry is safe by default. Ambiguous outcomes can't assume that.
A resolution path that isn't "wait forever." An unresolved state that never resolves is just a slower version of the same silent failure. Something — a reconciliation check against the downstream system, a human decision, a timeout that forces a conservative default — has to eventually move it out of "unknown."
Where this stands

I don't have all three of these built for StareBrain yet. Property 1 is roughly there conceptually (I don't auto-convert ambiguous results into success or failure). Property 2 is a stated rule, not enforced code. Property 3 doesn't exist yet — an unresolved action currently just sits flagged, with no forcing function to actually resolve it.

The useful thing that came out of this week isn't a fix. It's a sharper shape of the problem, arrived at by three people who weren't trying to solve the same thing and found the same wall anyway. That's usually a decent signal the wall is real, not a symptom of any one system being poorly designed.

If you've built the third property — an actual resolution path for "we dispatched this and don't know what happened" that isn't just a longer timeout — I'd like to see it.

Top comments (0)