Introduction
Vibe coding lets anybody construct an online app with nothing however a pure language immediate. You ask an LLM agent for what you need, and also you get a refined interface in seconds. The promise is that you will by no means have to have a look at the code once more, but that is removed from actuality. In actuality, our first immediate leads to an awesome UI, however asking for subsequent modifications begins breaking the app.
Silent Behavioral Failures
A big drawback from that is silent failures, the place the UI could seem nice at first look, however is damaged beneath the hood. For instance, clicking an “Add to Cart” button may present successful message and replace the cart depend on the display, but nothing is definitely written to the database or storage. To debug this, you need to work together with the UI, learn via logs, and in the end find yourself studying the generated code, which defeats all the goal of vibe coding.
We carried out a research the place we vibe coded real-world apps via iterative steps, and analyzed the ensuing silent failures. We noticed that even frontier fashions regularly introduce silent failures throughout iterations, and categorised them. These embody failures in monitoring state updates, cross-handler state disconnects, and disconnected UI suggestions (e.g success message is proven, however information was not saved).
Instance of Silent failure: The person asks an agent so as to add a promotional-code characteristic to a vibe-coded procuring utility. The agent creates a promo enter and an “Apply” button. When the person enters a legitimate code, the interface shows “Low cost utilized!” although the brand new whole is neither endured nor proven:
Why Present Verification Fails
If our app is damaged, asking brokers to debug typically results in false guarantees and remaining damaged code. And fairly often, the end-user typically doesn’t know what bugs are hidden within the code within the first place. In all these instances, present verification strategies are insufficient for vibe coders.
-
LLMs as Judges: Asking LLMs to self-debug, or determine bugs within the code is unreliable, as fashions can hallucinate or simply miss sure bugs. Even frontier fashions like Claude Opus 4.7, DeepSeek V3, and Gemini Professional regularly miss edge instances, hallucinate fixes, and fail to grasp complicated information flows.
-
Unit Assessments: Writing checks requires writing extra code to verify the generated code. It typically doesn’t verify UI-to-backend integration, It’s restricted to the precise situations you specify, and is inaccessible to non-programmers. LLMs might be able to write unit checks however it’s not assured to be full protection, and may typically be inadequate, and finish customers can not confirm.
-
Static Evaluation strategies: It’s deterministic, correct, and catches information circulate points completely. However the studying curve is excessive and writing in a static evaluation language or queries for the code is very complicated.
Introducing FlowCheck
By way of my analysis as a PhD pupil at Columbia’s DAP Lab, I developed FlowCheck. FlowCheck is a constraint language and static evaluation pipeline that allows you to simply specify how an app ought to behave instantly from the interface, and checks it in opposition to the precise code, with out ever having to learn a line your self.
Step 1: Specific your constraint
Customers present FlowCheck with their internet app’s path, which we open in a brand new tab. We show an overlay template of the shape “After I take [action], these replace: [component]” and customers can choose UI elements (and a detected checklist of APIs and storage) by clicking instantly on them, the identical manner they work together with their app.

Step 2: Translation to our language
We translate this template into a proper constraint in our language, utilizing a grammar we outline additional within the paper. The general format of our constraints appears to be like like: P(occasion | situation) = [0, 1]. This may be learn as, the likelihood of the occasion occurring (e.g a write taking place to a element), given a situation (e.g. button being clicked), is the same as 1 (at all times) or 0 (by no means).
For our instance above, we anticipate the full to be written to when the promo_input is utilized, which could be written as P(w(whole) | A(promo-input)) = 1.
Expressing this utilizing our language permits us to parse for the related info and compile it instantly down into static evaluation queries.
Step 3: Compilation to CodeQL
Now, we’ve a proper constraint like P(write(e) | motion(A)) = 1. FlowCheck parses this constraint and traverses its AST to extract key particulars (e.g which motion was triggered, what sort of occasion occurred (corresponding to a write), and which particular goal ingredient should be modified). From this, we are able to decide what queries to run. We are able to give it some thought this manner: our constraint merely signifies that when motion A is taken, occasion E occurs on all paths.
Subsequent, we use CodeQL. To offer some background, CodeQL is a static evaluation engine which takes our app’s code and converts it right into a queryable relational database. This enables us to run queries in opposition to the code. To catch silent failures, we focus totally on information circulate queries. CodeQL tracks information circulate from a supply (like a button click on occasion) to a sink (like native storage or a database replace). In FlowCheck, we maps the elements talked about within the constraint on to sources and sinks, and use this to kind queries that correspond to checks.
So, we compile our constraint down into two checks in opposition to this database: (1) path_exists question to confirm {that a} reachable path exists from the UI motion to the occasion, and (2) all_paths_write question to ensure that the write happens throughout each attainable execution path from A.
Step 4: Verification
Lastly, we run these CodeQL queries which verify the generated code in opposition to your constraints. If any constraint is violated (e.g the cart merchandise by no means updates storage), FlowCheck flags which side failed (corresponding to no dataflow from A to B) and the precise traces the place the violation happens.
Analysis and Evaluation
To guage FlowCheck, we used 4 internet functions modeled after well-known apps (Amazon, Twitter, Airbnb, Slack), which we generated through Claude Code. We wrote out a set of constraints that we anticipate to carry, and injected 30 refined, real-world information circulate bugs into these apps. We examined every of those constraints and located that FlowCheck accurately interprets and flags all 30 of our injected constraint violations with zero false positives.
For comparability, we used three frontier fashions (Claude Opus 4.7, DeepSeek V3, and Gemini Professional) as bug-finding baselines. We prompted them to search out the bugs in the identical damaged code, utilizing 3 prompts of accelerating element.
Immediate 1 (P1): “Here’s a internet app, much like [well known app]. Are there any bugs?”
Immediate 2 (P2): Lists the options the person requested, e.g., “the person requested an Amazon like app with these options: a product grid, a cart drawer…”
Immediate 3 (P3): Similar characteristic checklist as Immediate 2 plus an specific edge-case guidelines overlaying the varieties of bugs we added, incl. boundary values, all person states, all branches, and cross-handler consistency.

What Fashions Missed:
All three fashions confirmed considerably decrease accuracy and didn’t reliably catch all of the failures. Our greatest baseline (Claude Opus 4.7, utilizing probably the most detailed immediate) had solely a max of 26/30 (87%). By way of our evaluation, we noticed that fashions may catch easy localized errors, they constantly failed on cross-handler flows and conditional branches. As an illustration, in our Amazon app, an “apply promo” motion cleared the cart abstract whereas the checkout handler nonetheless tried to learn it. As a result of the handlers by no means referenced one another instantly, fashions evaluated them individually and didn’t hint the information circulate between them. Curiously, we discovered that including element to the prompts didn’t at all times assist. With extra element, fashions learn the code extra completely however grew extra keen to belief it, actively justifying bugs as intentional or unproblematic fairly than flagging them as errors.
Conclusion
Vibe coding stays difficult if builders and end-users must manually learn generated code to confirm the app aligns with their expectations. FlowCheck bridges this hole by translating UI-level intent into deterministic CodeQL queries, catching 100% of focused silent failures the place frontier fashions failed
Take a look at our full paper at https://arxiv.org/abs/2608.28880 for extra particulars on the compilation course of and constraint language! Please take a look at our github as properly at https://github.com/reyavir/flowcheck to attempt it out, depart a star in case you discover it useful or fascinating.















