The unit of work with Codex is not a long prompt or a beautiful diff but a contract of the result. A strong request for the agent need not be long; it must remove four kinds of uncertainty: what result is expected, where the boundary runs, what evidence is needed and what decisions the agent may make on its own. A vague "make auth better" removes none of these uncertainties - and almost guarantees drift, where the agent changes a lot and there is nothing to check.
The first uncertainty is the result. It is framed as observable behavior, not as an algorithm of typing code. "Add idempotency for POST /payments" is a result; "rewrite the handler" is a directive about the method that ties the agent's hands and prevents finding a simpler solution. The clearer the desired end effect is described and the less the path to it is dictated, the higher the chance the agent chooses a sensible implementation rather than literally executing your guess.
The second uncertainty is the boundary. Explicitly naming what must not change and which interfaces to preserve is cheaper than rolling back the extra later. "Do not change the public response schema and the database engine" is a boundary that keeps the change within safe limits. Without it the agent tends to touch the adjacent "while at it", and the very first such edit on an important contract creates more work and risk than it saves time.
The third and fourth uncertainties are evidence and decision rights. The verification is set concretely: a targeted integration test plus typecheck, not "make sure it works". And decision rights are limited by an explicit stop condition: if a new dependency or migration is needed - stop and explain the choice, do not make it silently. This turns a potentially irreversible decision into a point of control where you have time to intervene before, not after.
It helps to see such a contract in full once. Below is a compact form: what is needed, what not to change, where to start the exploration, what to verify with, where to stop and what to give at the end - files, behavior, checks, remaining risks. You return to this form each time your hand reaches to write one vague phrase: five minutes on a contract is cheaper than taking apart a beautiful but wrong diff produced by a request with no criterion.
An important rule is not to dictate an unconfirmed solution as fact. If the root cause is not yet known and you have already set the implementation, the agent will dutifully carry it out even if it misses. It is better to first ask for exploration without changes: trace the current request flow and find the existing transaction helpers, and only then choose. Letting the agent start with the real code rather than your hypothesis is the best way to avoid a beautiful solution to the wrong problem.
Need: add idempotency for POST /payments.
Do not change: the public response schema and the database engine.
First: find the current request flow and existing transaction helpers.
Verification: a targeted integration test + typecheck.
If a new dependency or migration is needed - stop and explain the choice.
Final: files, behavior, checks, remaining risks.The source of truth matters more than its retelling. A reproducible failing test beats the words "sometimes fails", a precise log beats a retelling of the error, an API schema or a type definition beats guesses about the format. The closer the agent is to the primary data, the less it guesses. The contract precisely directs it to the source: "first find the flow and the helpers" is a directive to go to the code rather than build a solution on an assumption about how everything works.
The typical failures around the contract are predictable. A vague goal with no observable result, because of which the agent drifts. The absence of a boundary, opening extra files and contracts. A verification of "make sure it works" instead of a concrete command. And silently accepting an irreversible decision without a stop condition. Remove all four uncertainties - result, boundary, evidence, decision rights - and the request turns from a wish into a task with an unambiguous sign of completion.