September 15, 2026
Eleven bugs from copied rules, and how we fixed them
A September review found eleven real bugs, all caused by the same rule written in two places. Here is what went wrong and how each rule now lives in one place.

genvi has six editors: the node canvas, the video composer, the image composer, the whiteboard, the 3D studio and chat. Each one saves your work, runs generations, undoes mistakes, shows prices and checks who you are. For a long time, each editor had its own copy of those rules. In September we counted what the copies had cost us: eleven real bugs.
What went wrong
Nobody wrote a clever bug. Each one happened because two pieces of code had to remember the same rule, and over time they remembered it differently. Here are some of them.
Video speed. The preview respected a clip's speed. The export did not. A clip set to 2x played fast in the preview, but came out twice as long in the final file.
Lost edits. Three editors waited a moment before saving. If you moved to another page during that moment, your last edits were dropped.
A failed generation that never failed. The code that streams a generation's progress back to you was written five times. In the whiteboard copy, a failure was ignored, so a failed generation stayed on "running" forever.
A lost result. When we picked up a stopped generation again, one part declined it, but another part marked it as received anyway. That deleted the only copy of a result you had already paid for.
Different sizes. When the agent made a text box, it was 280 by 60. When you made one on the board, it was 280 by 56.
The price. The cost of a model was worked out in seven places, each with different defaults. So the price shown to you and the price charged could differ. In one case, the screen assumed a 4 second video while the server assumed 5 seconds. Each side looked right on its own.
How we fixed it
Every fix has the same shape. We put each rule in one place that does only that job. It does not draw anything on screen and does not talk to other services. Everyone asks that one place. Where two areas truly need to differ, the shared part takes a small plug-in from each area. It never checks which editor is calling it.
This also makes the rules easy to test. The price calculator can be tested without Stripe, our payment company, or an AI provider. The rule for granting credits can be tested on its own too.
One answer for video
Four different pieces of code decided which clips were showing at a given moment, and the preview and export worked out the time differently. Now one shared part answers both questions. The preview and the export both ask it, so they cannot disagree.
We did the same for colour adjustments, which fixed seven adjustments that were being dropped on export. The part that combines the video shrank from over 1,300 lines to 539, because animations and transitions no longer existed twice. Automatic tests confirm that preview and export match.

One skyline, one set of rules: a city drawn once and seen from every side. Made with Seedream 4.
One price, one generation path
The steps that run a generation now exist once. Searching the code for the one step that sends a generation finds just one place. The price you see on screen and the price we charge both come from the same calculator. A check fails if they ever disagree.
We cleaned up billing the same way. Our payment handler had grown into a 698 line tangle that decided credits, sent emails and recorded revenue all in one place. It is now 70 lines that check the message and pass it on. The decision about granting credits is separate. Its exact behaviour is locked in by tests, so we could prove the new version does what the old one did.
Pages had the same problem
Our server routes (the parts that answer requests from the app) each checked who you are in their own way. Sign-in checks appeared 108 times. Fourteen routes had their own private admin check. Security worked only if someone remembered to add it.
Now one shared check covers every route but one. Errors also come back in one consistent shape, which replaced 589 hand-made error messages. Sixty of those used to show an internal message to the user.
What it cost
The main change, on 15 September, touched 329 files. Most of the weight was tests. We recorded how the old payment, credit and account deletion code behaved. That let us show each rewrite did the same thing, apart from the bugs we fixed on purpose.
We also decided to leave two things alone, and wrote down why so no one "finishes" them later by mistake. The video timeline keeps a copy of the playing time because only one part writes it and fifteen places read it. The settings panel stays hidden until opened because it holds a lot of state, including which API keys are currently showing.
How we keep it fixed
A shared rule only works if someone notices when it is skipped. So we added automatic tests. One fails if a route skips the shared check. Others fail if a displayed price differs from the charged price, or if saving loses its safe order.
Our rule now is short. If you are about to write a second copy of something, stop and find the first one. We spent weeks undoing that mistake.