Many people fight the model. They look at its output, say meh, complain, and then start interfering with how it works. They manufacture context for it instead of letting it figure things out. They decide how its files are laid out. They decide naming. They argue with it, reshape what it did, argue again.
All of that effort lands at the bottom of the funnel, on one output, and it's gone by the next run. The work belongs at the top of the funnel: loops, harnesses, evaluation criteria, supporting agents.
The difference between the two is what I mean by working with the model versus working against it.
What Fighting Looks Like
- Manufactured context: you hand-pick the files, paste in the summary and explain the architecture, because you've decided what the model needs to know. It could have gone and read the code.
- Dictated mechanics: where files go, how a function is split up, what a thing gets called. These pile up in a rules file, one complaint at a time.
- Argument as a workflow: "no, not like that" for the fourth time in a session, ending with you rewriting it by hand and calling that review.
Each of these comes from a reaction. You saw something you didn't like, and you reached in to change it. That's the habit to break. Working with the model means you don't dramatically react to the things you see and don't like.
Bad output still matters. It tells you something about your system. The useful question is never "how do I make it do this differently?". It's "what would have caught this, without me?"
Opus 6 Is Coming
My bet: Opus 6 is going to be released sometime soon. Then Opus 7. Then Opus 8. I think that's inevitable, and it changes the math on everything you do today.
Whatever you're doing, if it works with the models, it only gets better. A loop with a good evaluation at the end gets a stronger worker every time a model ships, and you barely touch it.
If it works against the models, you're injecting shit practices that make the better model work in a worse way. That rules file full of old complaints doesn't retire itself. It keeps steering the smarter model the way the last one needed steering. The context you hand-picked for today's model limits what the next one gets to see.
I think much of today's prompting is a fool's errand, because we're instructing AI to do things it very soon won't need to be told. Fighting the model is the same mistake with more effort behind it.
What the Top of the Funnel Looks Like
You build the thing that produces and judges outputs. In a few lines:
#!/bin/sh
# task.md says what done means. The gate decides if it's done.
failure=""
for; do
if out=; then ; fi
failure="The gate failed: "
done
Nothing here says how to do the work. task.md is the outcome. The gate is your judgment, written down -- tests, a lint, a benchmark, an invariant. When it fails, its output goes back to the model and the model deals with it. If every attempt fails the script exits non-zero and a person looks. It allows Bash, so run it somewhere disposable. Swap in the next model and this script barely changes.
Now the reaction has somewhere to go. Don't like what you saw? Add a check:
- Evaluation criteria: if it matters, it can be stated as a test or a measure. If you can't state it, ask whether it's a real requirement or just taste.
- Harnesses: the environment that runs the work and the checks, repeatably, the same way every time.
- Loops: failure feeds back in automatically, so you're not the one relaying error messages.
- Supporting agents: test writing and triage can run as their own agents, away from the main session.
One rule: keep the gate where the worker can't write to it, which is why it lives outside the working tree above. If the agent can edit the check, it isn't a check.
This is the same shift I keep coming back to. Software is becoming manufacturing, and on a factory floor you don't argue with a part as it goes by. You fix the line and you fix the inspection. Most of the job becomes forms of QA and forms of design, and this is what that looks like at the keyboard.
There's no Free Lunch
This has real costs, and it isn't always the right move.
A gate is only as good as what it checks. A weak one passes garbage with a green light -- a 200 is not a pass -- and writing a strong one is real engineering, up front, before you see any output. For a one-off change, just make the change. Nobody needs a harness to rename a variable.
Some things also deserve to be dictated, because they aren't the model's call: a public API contract, a data format other systems depend on, a compliance rule. Those are requirements, so put them in the gate, where they stay true for every model.
And you still have to read the output. If you can't tell good from bad, you can't write the gate.
Next time an output annoys you, go look at the line.