Write the Task Contract Before the Prompt
"Fix auth" defines neither the observable defect, nor the allowed scope, nor compatibility, nor the done condition. A task contract turns intent into data you can check before the agent runs and mechanically compare after it.
In the companion project the contract is a few fields of a type.
export interface TaskContract {
readonly goal: string;
readonly contextPaths: readonly string[];
readonly allowedWritePaths: readonly string[];
readonly constraints: readonly string[];
readonly acceptanceChecks: readonly string[];
}Every field answers a concrete question, and an empty field is a skipped decision.
Field · What to record · Example
- Goal - An observable change in behavior; 20 means a 20% discount, not a multiplier of 20
- Context - Files, symbols, test, error, canonical example; src/discount.js and the regression test
- Write scope - Allowed paths and explicitly forbidden zones; Only src/discount.js
- Constraints - API, versions, dependencies, security; Don't change the signature or add a package
- Done when - Commands and observable results; The focused test finished with code 0
- Stop conditions - When you can't continue on your own; Credentials, a destructive action, or a new scope are needed
Note the two fields people trip over most. Done when describes not intent but an observable result: the focused test finished with code 0, not "it works now." And Stop conditions names in advance when you can't continue on your own - credentials, a destructive action, or a new scope are needed. Without them the agent either stops in the wrong place or doesn't stop at all.
And a rule that guards against false confidence.
Don't pass a hypothesis off as a diagnosis. "The problem is the refresh token" is a direction to check, not a fact. Better: "after the session expires, login returns 401; check the refresh flow and reproduce the defect with a test first." The contract sets the shape, not a pre-decided answer.
The contract is stated. Now let's turn it into a prompt - not into pleading, but into an executable specification.
Why write the task contract before the prompt?