A field note from the AIdeazz AI Lab — a real incident on a live production system, written up from the logs. September 4, 2026.
Four generated films became the argument for the product instead of decoration. Every hard problem turned out to be a wrong choice of dimension — prompt adjectives instead of negative constraints, the video instead of the source frame, bezier coordinates instead of the real asset.
What it looked like from outside
A live API product page was two static gradient panels behind a form. The pitch was that AI answer engines must be able to read, parse and quote a site — an argument about the difference between a page as published and a page as machined — and nothing on the page carried it. Stock decoration would have said nothing, and a generic hero video would have been a cost with no argument attached. The team already owned a generative film pipeline built for an unrelated art project, and the question was whether it could produce brand assets that earn their bandwidth on a page that sells technical credibility.
What was actually happening
Not a defect — a build — but four separate stalls in it shared one shape, and each cost multiple rounds before the shape became visible. FIRST, prompting. A shot asking for "a blade sweeps down and cleaves it open, the cut face revealing the flesh" was honoured precisely: the model rendered a separate cut ring sitting beside a completely intact fruit, which satisfies "cut face" without anything being cut. Four rounds of richer positive description did not move it. SECOND, backgrounds. Generated clips kept arriving on a grey studio wall despite "pure black void" in the prompt, and the instinct was to mask the video — but the defect was upstream: the SOURCE still was already grey, because the text-to-image step had ignored the instruction and the image-to-video step faithfully inherited it. THIRD, drift inside a single clip: a fruit was whole and correct at one second and had degenerated into a rejected shape by 1.7 seconds, which reads as a prompt failure and is not one. FOURTH, the brand mark: eight rounds of hand-written vector paths, each refining a different wrong thing — coordinates, then stroke weight, then stroke-versus-fill, then the letterform construction — while the finished asset already existed as a file.
The fix
Each stall was resolved by changing the DIMENSION rather than the value. Prompting moved from describing the desired output to enumerating the undesired one: "there is only ONE fruit in frame and it is the one being split; no separate slice, no ring, no piece sitting beside it, nothing already cut" worked on the first attempt after four failures. Backgrounds were fixed at the input — the source still was masked to black and the clip regenerated from that, which produced a correct background immediately; two masking techniques were needed and they are not interchangeable, an elliptical alpha ramp cutting by WHERE a pixel is (right for subjects with pale centres) and a saturation gate cutting by WHAT a pixel is (right for coloured subjects on neutral ground, and it removes cast shadows too). Mid-clip drift was handled by extracting a strip of frames across every clip before assembly and trimming to the usable window rather than re-prompting. The mark was extracted from the existing asset with luminance as its alpha channel — a colour key would have cut a hard silhouette through the glow — and then used as a CSS mask over an animated gradient, because a static image cannot animate its own fill. Assembly stream-copies any cut already approved so nothing re-encodes, and the two encoding targets were measured rather than assumed.
How I know it worked
The measured encoding table inverted the expected answer and is the reason both targets exist: at desktop quality VP9 beat H.264 by about 17 percent on the same footage, while at mobile bitrates H.264 won by a similar margin and is additionally the only option one major mobile browser will play. A reference animation that four rounds of hand-tuning had failed to match was settled in one query by reading the computed style off the live reference site instead of eyeballing a screenshot — the difference was not colour but the keyframe: the reference travels one way and loops, the imitation oscillated out and back, and a gradient that runs out and comes back reads as a pulse rather than as flow. Deploys were confirmed by fetching the published bundle and grepping for a marker unique to the change, after a broader marker matched unrelated pages and reported a deploy that had not happened. Every number printed on the finished page was counted from production log lines, rounded DOWN with a plus so a running total can only become more true, and the window was proven by confirming the other logs held zero matching entries.
The rule this earned
When several rounds of refinement do not converge, the thing being refined is the wrong thing. Six times in one build the fix was a different KIND of change rather than a better value: negative constraints instead of better adjectives, the input still instead of the output video, trimming instead of re-prompting, the real asset instead of more coordinates, the element instead of its colours, the response body instead of the status code. Generative models make this trap sharper than usual, because they obey instructions precisely and the failure looks like disobedience — an underspecified prompt is not the model being wrong, it is the instruction admitting a reading nobody meant. The practical form: describe the output you keep getting and forbid it by name, and when three attempts along one axis all fail, stop tuning and change the axis.
The named concepts behind it
Naming a failure mode is what makes it possible to recognise the same shape somewhere new, before it costs another weekend.
Name the wrong output
A generative model that keeps producing the wrong thing is usually obeying you exactly — describe the failure and forbid it by name.
When a model keeps returning something wrong, the instinct is to describe what you want more richly. That instinct is usually wrong, and it is expensive, because each round feels like progress.
An underspecified prompt is not the model being wrong. It is the instruction admitting a reading nobody meant. Positive description narrows toward one good output. It does nothing to close off the bad ones — and if a bad reading satisfies every word you wrote, more words in the same direction will not exclude it.
A real example: a shot asking for "a blade sweeps down and cleaves it open, the cut face revealing the flesh" produced a separate cut slice sitting beside a completely intact fruit. Every clause was honoured. "Cut face" was satisfied without anything being cut. Four rounds of richer description did not move it. What worked on the first attempt was naming the failure:
"There is only ONE fruit in frame and it is the one being split; no separate slice, no ring, no piece sitting beside it, nothing already cut."
The move is mechanical once you see it: take the output you keep getting, describe it plainly, and forbid it. Not "make it more X" — "it must not be the thing I just received."
This is why negative prompts exist in image and video tooling, but the idea is not specific to them. It applies to any instruction-following system:
- LLM extraction that keeps returning a summary instead of a quote — forbid paraphrase explicitly, don't ask for "more faithful" quotes.
- Code generation that keeps adding a dependency — say which approach is banned, not just which is preferred.
- Classifiers that keep choosing a plausible neighbouring label — name the confusable class and rule it out.
- Agents that keep taking a reasonable but unwanted action — enumerate the action, don't reweight the goal.
Two cautions. Negative constraints are cheap to add and easy to over-apply; a wall of prohibitions crowds out the actual request and can suppress the good output along with the bad. And a constraint only works if it names something the model can recognise — "not ugly" forbids nothing, while "not two objects, not symmetrical" forbids something specific.
The general habit this belongs to: when several rounds of refinement along one axis do not converge, the axis is wrong. Adjectives, temperature and length are all the same axis. Prohibition is a different one. So is fixing the input instead of the output, and so is abandoning generation for an asset you already have.
Verify from logs, not config
Configuration tells you what somebody intended. Logs tell you what happened.
A setting, an environment variable or a present API key is a statement of intent. It is evidence that somebody meant for a behaviour to occur. It is not evidence that the behaviour occurs.
The gap between the two is where the longest outages live, because reading the configuration feels like verification. It produces confident, wrong statements: the key is set, so the provider works; the schedule says every fifteen minutes, so it runs every fifteen minutes; the file was deployed, so the new code is running.
Each of those has a cheap, decisive check that costs seconds:
- Probe the dependency, do not read its credential. A key that exists proves nothing about the balance behind it.
- Grep for the action line, not the setup line. A startup banner proves the process started, not that it ever did its work.
- Compare timestamps after a deploy. If the running process is older than the file on disk, it is still executing the previous version from memory.
The rule this earns: never report a system's behaviour from its configuration. Grep the line that proves the behaviour happened, and quote it.
The render is the artifact
What a JavaScript-executing consumer stores is the DOM after execution, not the HTML you served. Anything that exists only before execution was never delivered.
This is one step past git is not the origin. There the receipt lied about what was being served. Here the serving is genuinely correct and current -- and still the consumer stores something else, because it transforms your bytes before reading them.
Modern search crawlers execute JavaScript. The thing that lands in the index is the DOM after that execution, not the file that came off the wire. Two consequences follow, and both are counter-intuitive because the served file looks perfect in curl and in the browser's View Source.
First, content that exists only in the pre-execution HTML is invisible. is the sharpest example: by specification its contents render only when scripting is disabled, so a crawler running with JavaScript on discards it. A block is a fallback for the absence of JavaScript. It is not a crawler channel, even though a crawler that does not execute JavaScript will happily read it -- which is exactly why the technique appears to work when you test it with a plain fetch.
Second, anything the application rewrites at runtime wins. If the page ships a carefully-chosen and then the client-side router sets document.title on mount, the served title never reaches the index. Both values are "correct" in their own file. Nobody wrote a bug. The two simply describe the same page differently, and the one that executes last is the one that counts.
The result is a split audience. Consumers that execute JavaScript see one page; consumers that do not -- many AI crawlers, link-preview bots, plain HTTP clients -- see the other. Optimising for one can silently be measured with a tool that reads the other, which is how a page earns a perfect score on precisely the content its most important reader throws away.
Ordinary-life version: you post a letter with a covering note clipped to the front. The recipient's mailroom removes every clip before delivery. Your letter arrived. Your note never existed, as far as the reader is concerned -- and photographing the envelope on your desk will never reveal that.
Defences:
1. Diff the two representations deliberately. Fetch the URL raw, then fetch it through a renderer, and compare title, description, canonical and word count. If they disagree, decide which one you meant -- do not let execution order decide for you.
2. One definition, both surfaces. If a static build writes head identity and the client also sets it, both must read the same source file. See single source of truth.
3. Verify from the consumer's stored copy, not your own. The decisive evidence is what the index actually holds. String-match it against your source: whichever file it matches character for character is the one that is really shipping.
4. Know which half your instrument reads. A checker that fetches raw HTML measures the non-executing audience. That is a real audience and a real score -- but it is not a verdict about a rendering one.
---
This note is one entry in a running wiki of production engineering lessons — every concept linked to the incident that taught it — at aideazz.xyz/ai-ops-wiki.html.
No customer data, credentials, hostnames or internal record identifiers appear in these write-ups.