Before Code: Turning Intent into a Buildable Contract

For addyosmani/agent-skills, the first engineering problem is often not implementation but interpretation. This chapter follows how the repository moves from an uncertain request to confirmed intent, a specification, and a dependency-aware plan. The useful distinction is between the artifacts that decide what should be built and the later evidence that proves the chosen implementation works.

An agent can begin coding immediately, but immediacy is not the same as alignment. A phrase such as “add a dashboard,” “make this faster,” or “support team accounts” leaves important variables unstated: who needs the result, what decision it should enable, which existing behavior must remain, and what would count as success. If those variables are filled in silently, the agent may produce a coherent solution to a problem nobody meant to ask.

A hypothesis makes clarification testable

The repository’s interview workflow begins with a hypothesis and a confidence number. That may sound like a small conversational preference, but it changes the direction of the exchange. Instead of presenting a blank survey, the agent says what it currently thinks the user wants and identifies what is missing. The user can correct a concrete interpretation more easily than they can generate a complete requirements document from scratch.

The confidence number is useful only when it is honest. A low number must be accompanied by a reason: perhaps the audience, success condition, or binding constraint is still unknown. That makes uncertainty actionable. The next question can target the largest gap rather than collect details indiscriminately.

The workflow also insists on one question at a time, with a guess attached. Asking several questions at once encourages shallow answers and prevents the next question from responding to the first answer. The attached guess exposes the agent’s own assumption, which is the object the interview is trying to surface. It gives the user something to accept, correct, or reject.

The repository’s example makes the point concrete. A user asks for a dashboard “for our metrics.” The agent first guesses that an engineering team needs a stand-up view. The user explains that the real problem is personal experiments spread across documents, and then clarifies that they do not even have a reliable list of experiments. The intended artifact shifts from dashboard to list. No framework decision or component design could have repaired that misunderstanding after implementation without paying rework cost.

The interview ends with a restatement that names the outcome, user, reason now, success condition, constraint, and out-of-scope work. An explicit confirmation matters because “sounds good” may only mean that the user wants to move on. The artifact being produced here is not a plan and not code. It is shared intent.

Ideas need a boundary before they need a plan

When the request is not merely unclear but still exploratory, idea-refine supplies a different kind of discipline. It first reframes the raw idea as a “How Might We” problem, asks who the idea is for and what success looks like, and then generates a limited set of meaningful variations. The purpose of divergence is not to create an impressive list. It is to expose alternative directions before the team quietly commits to the first conventional answer.

The lenses are deliberately practical. Inversion asks what would happen if the usual approach were reversed. Simplification asks for the version that is ten times easier. An audience shift asks whether a different user would reveal a different problem. An expert lens asks what a specialist would treat as obvious. These variations are useful only if they are grounded in the existing codebase or operating context when one exists; otherwise they become free-floating product imagination.

Convergence then tests the remaining directions against user value, feasibility, and differentiation. Most importantly, it surfaces what each direction assumes, what could kill it, and what the team is choosing not to do. The Not Doing list is not an administrative appendix. It marks the boundary that keeps a promising idea from becoming a bundle of unrelated capabilities.

This is where interview and ideation differ. Interviewing tries to discover what the user actually wants before the agent chooses a direction. Ideation expands a now-clear problem into options and helps the user select a tractable one. Both resist silent assumption, but they operate at different points in the decision state. Confusing them can produce either premature narrowing or endless brainstorming.

A specification is a contract for decisions, not a promise about the code

Once intent is concrete enough, spec-driven-development turns it into a structured specification. The specification covers the objective, executable commands, project structure, code style, testing strategy, and boundaries. Those areas force different kinds of clarity. The objective names the user and desired outcome. Commands make the development environment observable. Structure and style connect the new work to existing conventions. Testing says how behavior will be assessed. Boundaries distinguish what is always allowed, what requires approval, and what must never happen.

That last distinction is particularly important for agent work. A vague instruction such as “build a secure feature” does not tell an agent what to do when security conflicts with convenience. A boundary such as “validate external input,” “ask before changing the database schema,” or “never commit secrets” gives the agent a decision rule. It also gives a reviewer something concrete to inspect later.

The specification should translate broad requests into measurable success conditions. “Make the dashboard faster” is not yet a target. A useful specification might define a load-time threshold, a response-time expectation, or a condition about layout stability, but the chosen numbers must be agreed rather than invented as if they were facts. This turns a vague aspiration into something an implementation can approach, test, and revise.

The specification remains alive after approval. If the data model changes, the specification should change before the code. If scope changes, the out-of-scope boundary must change with it. This does not make the document a forecast that must never be wrong. It makes it a visible record of the current contract, so that a later implementation decision is not silently based on an obsolete assumption.

Capability maps prevent oversized specifications

A single request sometimes bundles several independently testable capabilities. Identity, billing, notifications, and reporting may all belong to one product initiative, but they have different consumers, data, and failure modes. The repository asks for a small capability map before writing detailed module specifications in that case.

The map establishes stable module identifiers, dependency direction, and build order. If two proposed modules each require the other, that is evidence that the boundary may be wrong. If reporting depends on billing and notifications, the dependency is made explicit before tasks are written. The map is not a full project plan; it is a decision about the shape of the problem.

This gate prevents a common form of planning debt. If a monolithic specification is written first, its boundaries become socially expensive to change. Every later task inherits the ambiguity, and the agent must repeatedly reason over capabilities that are unrelated to the current slice. A ten-line boundary decision can be cheaper than revising a long document after implementation has already coupled the modules.

Human review belongs at this point because the cost of a wrong boundary grows rapidly after code exists. The specification workflow therefore treats approval as a gate between phases. An agent can prepare a clear proposal, but it should not silently turn its own interpretation into the project’s contract.

Planning follows dependencies, then cuts vertically

With the specification accepted, planning maps what depends on what. The repository uses a dependency graph to identify foundations, interfaces, and consumers, but it does not recommend building the entire system one horizontal layer at a time. Building every database table, then every API endpoint, then every interface often leaves the system untestable until the end.

Instead, the preferred unit is a vertical slice: one complete path through the relevant layers that leaves working behavior behind. For an account feature, one slice might establish the smallest path for registration, including the necessary data, endpoint, interface, and focused tests. A later slice can add login, then another can add a related user action. The exact product is not the point. The slice is valuable because it gives the team an observable outcome and a narrow place to learn.

Planning can also be contract-first when different parts of a system need to proceed in parallel. The interface is agreed before the backend and client diverge. It can be risk-first when the most uncertain element—such as a connection, migration, or external dependency—should be proven before the rest of the feature is built. These are different ways to reduce the cost of discovering a foundational mistake late.

Each task needs acceptance criteria and a verification step. Acceptance criteria answer, “What must be true when this task is complete?” Verification answers, “What observation will establish that it is true?” A task such as “add endpoint support” is too vague. A bounded task names the behavior, the relevant error path, the test or build command, and the files or boundary likely to change. It also has dependencies and an estimated size that can be discussed.

The two statements should be related without being identical. “A user can submit a valid registration and receives a usable account” is an acceptance condition. “The focused test exercises a valid submission, asserts the returned account state, checks the expected failure for invalid input, and the build passes” is a verification path. The first protects the purpose of the task; the second prevents the team from declaring success because the code merely exists or compiles. This separation also makes disagreement easier to resolve. A human can accept the desired behavior while asking for a different test, or accept the test method while rejecting the behavior as out of scope.

Task size is part of the control mechanism. A small task is not valuable because small numbers are morally better; it is valuable because a failure has a narrow search space. If a task touches several independent subsystems, its acceptance criteria tend to hide multiple decisions behind one sentence. Splitting it exposes those decisions and creates more points at which the agent can return for clarification instead of improvising across the whole system.

Checkpoints after a small group of tasks keep the plan from becoming a second monolith. They ask whether tests pass, the application builds, the central flow works, and the human is ready to proceed. High-risk tasks should appear early enough to fail fast. A task that touches several independent subsystems or cannot state its acceptance criteria in a few precise points should be split again.

sequenceDiagram participant U as User participant I as Intent interview participant S as Specification participant P as Plan participant G as Human checkpoint U->>I: Submit an uncertain request I->>I: State hypothesis and ask one focused question I-->>U: Restate outcome, constraints, and non-goals U->>S: Confirm intent S->>S: Define success, boundaries, and test strategy S->>P: Identify capabilities and dependencies P->>P: Slice work and assign acceptance plus verification P-->>G: Present bounded plan G-->>P: Approve, revise, or stop

The contract reduces wrong-direction work; it does not prove the result

This distinction keeps the Define and Plan layers honest. A confirmed intent can still describe an unwise product decision. A well-structured specification can still contain a mistaken assumption about an existing API. A dependency-aware plan can still choose the wrong implementation. Those possibilities are reasons to make the artifacts explicit, not reasons to pretend that planning is proof.

The artifacts do, however, change the cost of correction. Before code exists, a user can reject the inferred audience or narrow the scope. Before a whole feature exists, a planner can expose a dependency cycle or move a high-risk task forward. Before a slice grows, acceptance and verification can reveal that the team has not decided what “working” means. The next boundary is therefore context: the agent must carry this contract into the actual codebase without flooding itself with irrelevant material or trusting every piece of external text it encounters.

References