James Wiesebron

Intentional agentic work · Intent

Give agents a contract, not a conversation

When intent lives in a chat, agents drift. Write it down somewhere they can't rewrite.

In most agent setups, the only record of what you asked for is the conversation. The agent's understanding of the task lives in its context window, which grows, gets summarized and eventually gets replaced. Another agent can't read it. You can't audit it. And when the agent decides partway through that the task really means something slightly different, nothing anywhere disagrees with it.

That's fine for a quick question. For real work, the intent needs to live somewhere that outlasts a chat, somewhere the agents read from, report back to, and can't rewrite. I call that place a contract.

What goes in it

A contract is a structured document that sits outside any agent's conversation. It holds the goal and the phases the work moves through. It breaks each phase into tasks, and every task carries acceptance criteria and the areas of the work it expects to touch. It also lists every decision that belongs to a person, along with that person's answer once there is one.

Every agent that joins the work starts from the contract, and every agent records its progress back into it as it goes, not in a burst at the end. The conversation is still how agents think. The contract is what everyone agreed to.

How much of the path to spell out

A contract isn't a full plan for the agent. Your part is mostly the goal: what done looks like, and how you'll check it. A lot of the implementation underneath doesn't matter to you, like which file a helper lives in or how a loop is written, as long as the result meets the criteria. Spelling those out costs you time and doesn't give you anything better to check.

The judgment is knowing where the path does matter, and whether the agent can find a reasonable one on its own. For a familiar kind of change in code it can read, it often can. For anything that touches a public API, a migration, security or someone else's work, or that depends on context the agent can't see, spell out the part that matters, or say what you'd object to. An out-of-scope list (no changes to the public API, no new dependencies, nothing that needs a migration) bounds the route without dictating it. And if a route reaches the goal in a way you wouldn't accept, the contract was missing something. Add it there, so the next agent reads it too, instead of correcting this one in chat.

Each role owns part of it, and only part

A shared document that anyone can edit isn't much of a contract. What makes it hold is that each role can change only the fields it owns, and the system enforces it, so no prompt has to.

The agent doing the work can record what it did, link the change to its task and leave notes. It can't mark its own task complete. A reviewer can mark tasks and phases done, but it can't touch the work itself. Only a person can resolve a decision. Once the contract is created, nobody can go back and rewrite its scaffolding. An agent can't edit the plan it's in the middle of executing.

This is the same idea as removing capabilities instead of writing rules. "Don't approve your own work" is a suggestion. An implementer that has no way to set a task's status doesn't need to be told.

Decisions are part of the contract

The most important thing a contract tracks is which questions are yours. When an agent hits a real choice, like a scope question, a tradeoff or anything a reasonable person would want a say in, it adds a decision to the contract instead of resolving it in its head.

Decisions can be added but never overwritten. A phase can't close while any of its decisions are open. When an answer changes the picture, the phase runs again with that answer in hand, and it only moves forward on a pass that raises nothing new and that a person approves. Answers carry forward, so a decision that's already been made isn't asked again. There's no automatic "advance after three rounds" fallback either. If the work genuinely won't converge, that's worth an alert and a human look, rather than a push forward with open questions.

"Nothing to decide" is a claim

Agents look for the path of least resistance, and the easiest decision to make is the one you never mention. If "there's nothing here for you to decide" costs one sentence, you'll get that sentence a lot.

So an empty list of decisions has to be backed up. The agent lists the questions it considered and says, for each one, why it isn't yours to make or why it's better asked later. That itemized list is what you confirm, so you're checking specific reasoning instead of a paragraph. Anything deferred travels with the work into the next phase, which has to either bring it to you or explicitly set it aside. Putting something off becomes a handoff.

Reviewers have a job here too. A draft that commits to a choice that belongs to a person gets sent back, even if the choice it made is a good one.

Catch it at the plan

The contract is also where you catch mistakes while they're cheap. Redirecting an agent before it starts costs minutes. Redirecting it after a day of implementation costs a day.

That's why the plan is a phase with its own gate, and why plan reviewers do more than read for sense. They check that everything the plan depends on actually exists, in the place where the work will run. A plan can be perfectly reasonable on paper and still assume a tool, a data source or a permission the executing agent will never have. The more you constrain agents, the more important this check becomes, because the planner can't see the walls unless someone looks for them. I've watched a confident plan burn most of a day of agent time on that, when one quick search during planning would have caught it.

When alignment depends on the conversation, it lasts only as long as the context does. A contract doesn't guarantee the agent reads it correctly or notices every question that should go to a person. What it does is make drift visible and bounded. Fields have owners, decisions are itemized, plans get checked against reality, and divergence surfaces at a gate instead of at the end. That's a big part of what I mean by runtime alignment.