Vibes DIY
Vibes DIY / Blog
From the build log

No negative tokenization

Don't think about an elephant.

Too late — and that's not a quirk of human psychology that language models happened to inherit. For a next-token predictor it's closer to arithmetic. A model writes code by continuing the context it's been given, weighted toward the patterns most present in that context. Write "never import shared values from App.jsx" into a system prompt and you have just shipped the exact tokens of the mistakeimport, shared values, App.jsx, arranged in precisely the shape you fear — into every single generation. The word "never" is one token of sentiment stapled to a fully-worked spelling of the bad idea. You wanted a guardrail; you built a beacon.

So our code-generation prompts run under one house rule, enforced harder than any style guide: no negative tokenization. We never tell the model what not to do. We show it more of what to do, until the good shape is the one it reaches for by default.

A lone elephant, rendered in a green duotone — the title card for a post about not thinking about elephants.
The pink elephant, in the room: a prohibition names the very thing it hopes to prevent.

The import that went in a circle

Here's the rule earning its keep on a real bug. A generated multi-file app bricked itself with one innocuous-looking line: the app's components imported a shared constant back out of App.jsximport { STAGES } from "../App.jsx" — while App.jsx imported the components. A circular import to the composition root. The app went to a grey grid the moment generation finished and hung on its loading animation forever after.

Every instinct says: add the rule. Never import from App.jsx. Avoid circular imports. (We can say it here — you're not the model.) That fix would have put "import from App.jsx" into the standing vocabulary of every future generation, forever, attached to a "never" the model may or may not weigh in the moment.

What shipped instead was a worked example of the good shape, in the prompt section that teaches multi-file apps: shared values live in a small leaf module, and everything — App.jsx and components alike — imports from the leaf, so the dependency arrows all point one direction:

jsx// lib/stages.js — the leaf: shared values live here, in their own small module
export const STAGES = ["backlog", "in-progress", "done"];
jsx// App.jsx (at the root) imports the leaf...
import { STAGES } from "./lib/stages.js";
jsx// ...and so does each component, by the path from its own location
import { STAGES } from "../lib/stages.js";

Every arrow points at the leaf: App.jsx imports the feature components, and both sides import the shared module the same way. Acyclicity isn't stated as a law; it's structural in the example — you can't copy that shape and produce a cycle. The steer that settled it, verbatim from the owner mid-fix: "we don't do negative tokenization — we just make more good examples."

Get posts like this in your inbox

One email field. Real updates. No algorithm required.

The prohibition that became a litmus

The subtler version of the same discipline showed up in our access-control prompts. We'd concluded that owner-only generated apps were a latent bug — they work perfectly for the person who made them and are silently broken for every visitor. The obvious sentence to write: don't generate owner-only apps.

That sentence names the anti-pattern (salience, again) — and it's also just weak pedagogy, because it doesn't tell the model what success looks like. What went into the prompt instead is a positive litmus: when a stranger opens this app, can they do the thing it's for? That one affirmative question makes shareable-by-default the natural reading, and it still gracefully permits the apps where the stranger's job really is just to read. The broader discipline has a slogan of its own around here: examples bias, grammar enables — feature the outcomes you want as worked recipes, describe the primitives neutrally as grammar, and let the model reach the rare edges by composition rather than by studying a list of your fears.

Fix the example, not the rule

The corollary that took longest to internalize: when generation keeps getting something wrong, the highest-leverage fix is almost never new prose. The model copies the nearest complete example far more faithfully than it obeys any abstract rule — so you fix the example, or you add one.

Our per-skill eval lanes taught us this quantitatively. When one app archetype stubbornly resisted a behavior we wanted — a habit tracker that wouldn't come out shareable about half the time — more explanatory prose moved nothing. The lesson we wrote down: a sticky case needs a worked example, because examples are what generation actually consults. Same finding from our capability docs: if a doc shows how to call a feature with fresh input but not what the stored form looks like, the model doesn't stop at the boundary of its knowledge — it hallucinates the missing middle (we caught generated apps writing an elaborate no-op where the undocumented step belonged). The doc checklist now requires the full round trip — fresh input, stored form, display — so there's no gap for imagination to fill.

There is one narrow, principled exception, and it proves the mechanism rather than breaking it: when we've observed a specific hallucination in the wild, its doc gets an explicit callout of that mistake. Naming a pattern the model already produces doesn't create an attractor — it corrects one that exists. The rule is really: never name a mistake prophylactically. Prohibition is a treatment, not a vaccine.

The gate that says "don't" to "don't"

Style guides decay unless something enforces them, so we turned this one into a tripwire. We run an autonomous improvement loop over the prompt corpus — an agent proposes an edit, a frozen grader scores a batch of generated apps, and the edit is kept or discarded on the numbers. One of the loop's discard-gates reads the added lines of the proposed prompt diff and rejects edits that enumerate prohibitions or name an anti-pattern. The machine that's allowed to rewrite our prompts is not allowed to say "don't" either.

Two honest notes about that gate, because a tripwire you overstate is worse than none. It's two layers: a pattern match for the obvious tells (never, don't, avoid, must not, plus the specific anti-pattern names we've been burned by), and then — only if that comes back clean — one small judge call asking whether every added line teaches by affirmative example. And it deliberately fails open: if the judge is unreachable, a clean pattern match still passes the edit. So it catches the shapes we know and the phrasings the judge is around to catch; a creatively-worded prohibition could still slip by. It's a seatbelt on a habit, not a proof.

And the affirmative diet holds up under measurement. In the access-model campaign, the share of generated apps that a second visitor could actually use went from 82.8% to roughly 90% (91.7% on a holdout set), while the consent-safety score held its floor — and every one of the winning edits was phrased affirmatively. Nothing about this rule is a handicap. The good examples simply win.

Where "don't" lives instead

None of this means the platform runs on hope. Some properties must hold always — security boundaries, spec correctness, things a beautifully-biased example can only make likely. Those never go in the prompt at all. They're enforced in the deterministic layer that processes every app at deploy time, where a rule is a rail, not a request — which is the same structural instinct that runs the rest of the platform: the prompt teaches taste; the runtime owns invariants. The prompt is a lesson plan, and you don't waste a lesson on something the building already makes impossible.

Which is, in the end, why this rule feels less like machine-whispering and more like something every good teacher already knows. A ski instructor doesn't shout "don't look at the tree" — you'd steer straight into it. They say look where you want to go. We spend our days schooling a code generator, and the deepest thing it has taught us runs the other way: attention is the steering wheel, for models and for people. Fill the context — any context — with vivid, complete pictures of the thing done right.

See what the good examples build

Describe an app in plain language — the lesson plan does the rest.

Start building →

Enjoyed this? Get the next post by email

One email field. Real updates. No algorithm required.

Prefer a feed? RSS · Atom