inkspell
writing topics feed
MODE
THEME
← writing

What a Useful Specification Has to Do

A useful specification must compress intent, supply the right context for the current decision, and learn from code without becoming a copy of it.

July 30, 2026 · 9 min read
ai agents specification

The modern specification has acquired a constitution, a workflow, several committees, and—if left unattended—a foreign policy. Before it applies for statehood, what work does it actually have to do?

Three jobs, as far as I can tell.

In the first article, I argued that a specification earns its keep only when it makes software easier to change. That gives me a way to judge the result, but not a method. A six-page document can be useless. So can a six-line one. A pristine specification can describe last month’s system perfectly while today’s code wanders off unsupervised.

Checking whether the code works is not enough. Tests and retries may eventually produce a valid implementation even when the specification provides poor guidance; the result does not tell me how costly the path was or whether the decisions needed for the next change were preserved.

The useful question is not whether a team “does specification-driven development.” It is what knowledge the specification preserves, when that knowledge appears in front of the person or agent making a decision, and what happens when implementation teaches me something the specification did not know.

Compactly:

Compress intent, allocate context, reconcile discoveries.

These are not three stages in a new ceremony. They are three needs that a useful specification must balance.

A specification compresses intent

Code contains more than intent. It contains framework mechanics, library constraints, arbitrary choices made where the requirements were silent, temporary workarounds, and the occasional mistake that survived long enough to acquire seniority.

Suppose a subscription can be cancelled at any time. The user keeps access until the end of the period already paid for, and the subscription must not renew.

That is durable intent. An implementation may express it with a status enum, a scheduled job, a payment-provider flag, or a small creature living in a message queue. Those choices matter while building the feature, but they are not automatically part of what the feature means.

A plan records how I intend to make the change. A specification records what must remain true after the change is complete.

The specification should preserve the decisions that must survive a rewrite while leaving the rest fluid. Too little detail and two implementations can behave differently where only one behavior is acceptable. Too much detail and the specification becomes a sprawling prose copy of the code: all the implementation detail to maintain, with none of the tooling that makes code manageable.

The flexibility a specification permits tells me what it has compressed. Names and algorithms may vary without changing behavior. But if one cancelled subscription loses access immediately while another retains it until the paid period ends, either one implementation violates the specification or the specification has left a product decision unresolved.

So compression and sufficiency pull in opposite directions. Shrink the specification far enough and intent disappears. Expand it until every implementation choice is fixed and the specification preserves the very accidents it was meant to leave behind.

The target is neither endpoint:

Preserve the smallest model that rules out unacceptable differences in behavior without freezing incidental implementation choices.

The complete specification is usually the wrong context

Once useful intent has been preserved, another problem appears: which part belongs in front of the agent now?

The whole specification may be the complete description of the system and still be the wrong context for one decision. If I am changing the cancellation button, I probably need the access and renewal rules. If I am correcting a typo on the home page, I do not. A document can be worth keeping without being worth loading.

This is where “use less context” becomes dangerously easy advice. The smallest opening prompt is not necessarily the cheapest way to finish the task. Omit a rule and the agent may search for it, ask for clarification, choose a default, discover the mistake during review, and rewrite the feature. The prompt was tiny. The work was not.

The cost belongs to the whole path to a valid result:

Six labeled rectangular cards add initial context, retrieved context, corrections, retries, review, and repair into the total cost of reaching a valid result. Six labeled rectangular cards add initial context, retrieved context, corrections, retries, review, and repair into the total cost of reaching a valid result.
Opening context is only one part of the cost; retrieval, correction, retries, review, and repair count too.

That gives me a stronger rule than “load fewer tokens”:

Prefer the least costly context that produces valid work reliably.

Token count is part of that cost, but it does not tell me whether the context was useful. Better signs of missing or harmful context are repeated searches, late discovery of a constraint, decisions that have to be reversed, unsupported assumptions, and repairs that should not have been necessary.

“Complete” is therefore relative to the decision, not the project. One task may need rules from several parts of the system. One rule may govern dozens of tasks. A neat document hierarchy will not always match that many-to-many relationship.

Sometimes the simplest selector is no selector at all. If the specification is small enough, loading the whole thing may cost less than dividing it; context allocation becomes a problem only when selection is cheaper than carrying everything.

This is also where elaborate context architecture can become its own hobby. Before inventing a selector, metadata language, dependency graph, and ceremonial hat for the dependency graph, I would learn how real tasks use context and only then decide how to organize it.

Implementation is allowed to teach the specification

A specification that never changes will eventually become wrong. A specification that absorbs every detail from the code will eventually become the code.

Reconciliation is the path between those failures.

When the specification is right, fix the implementation

If cancellation removes access immediately despite the promised paid period, the implementation is wrong. Updating the specification would merely legitimize the defect.

When the product decision changes, update the specification

If implementation reveals that the selected payment integration can only cancel immediately with a prorated refund, and the product accepts that tradeoff, the specification should record the new behavior before the implementation adopts it.

Most implementation discoveries should remain implementation details. Reconciliation is not copying code into Markdown; it is deciding whether an implementation discovery should constrain future implementations.

Too little reconciliation produces drift: the next agent repeats an old mistake because the accepted knowledge never reached the durable intent. Too much produces bloat: one implementation’s field names, workarounds, and architecture become permanent law.

A good reconciliation process therefore has both recall and restraint. It keeps discoveries that matter to future decisions and rejects details that only explain the current implementation.

Three panels show several source documents compressed into one durable specification, selected rules copied from a larger specification into the current task, and an implementation discovery routed either into the specification or kept in the implementation. Three panels show several source documents compressed into one durable specification, selected rules copied from a larger specification into the current task, and an implementation discovery routed either into the specification or kept in the implementation.
Compression preserves the promise. Context allocation gives the task only what it needs. Reconciliation keeps the discovery and drops the debris.

Current workflows divide the three jobs differently

Two prominent current specification-driven development (SDD) workflows make useful examples: GitHub Spec Kit and OpenSpec. Their mapping to the three jobs is loose. Neither workflow uses these categories, and one artifact may serve more than one job.

GitHub Spec Kit separates the what and why in spec.md from the technical plan and dependency-ordered tasks. Clarification, requirements checklists, cross-artifact analysis, and convergence checks help preserve and verify intent. Task decomposition and staged implementation provide smaller units of working context.

Reconciliation is left to the team. Spec Kit documents a flow-back spec, a flow-forward spec, and a living spec, each with different rules for how specifications, plans, tasks, and implementation inform one another over time.

OpenSpec keeps the current specification separate from proposed changes. Delta specifications compress a change instead of repeating the whole system, while proposals, designs, and tasks organize context around one unit of work.

OpenSpec makes reconciliation more explicit. Artifacts remain editable during implementation, and archiving folds accepted delta specifications back into the current truth.

Compression and reconciliation have fairly visible homes in both workflows. Context allocation is less direct: decomposition, deltas, and change boundaries may help, but only actual use can reveal whether a particular task receives the least costly context.

The three jobs constrain one another

Optimizing one dimension alone breaks the others. Aggressive compression creates missing context. Loading everything hides a good specification inside irrelevant detail. Indiscriminate reconciliation destroys compression. Refusing to reconcile makes careful context allocation serve stale intent.

That is why I would not judge a specification by its length, completeness, or mere existence. I would ask three questions:

  1. Does it preserve the decisions whose loss would permit unacceptable behavior?
  2. Can each task get the decisions it needs without inviting the whole specification along?
  3. Does it learn durable facts from implementation without preserving implementation by accident?

This is not a recipe for the ideal specification. It is a way of thinking about the tradeoffs that make one useful. The balance depends on the system, the task, and the cost of being wrong.

The first article gave the specification one job: make software easier to change. This one gives that job a shape.

Keep the intent worth preserving, load it when the decision needs it, and let implementation teach the specification only what the next implementation should still know.

← Back to all writing
Last login: Thu, Jul 30, 00:00:00 on ttys001
user@inkspell:~/blog read what-a-useful-specification-must-do.md
FILE: what-a-useful-specification-must-do.md DATE: 2026-07-30 WORDS: 1623 READ: ~9m
TAGS: [ai] [agents] [specification]

What a Useful Specification Has to Do

// A useful specification must compress intent, supply the right context for the current decision, and learn from code without becoming a copy of it.

» BEGIN OUTPUT
cat <<'OPENING'

The modern specification has acquired a constitution, a workflow, several committees, and—if left unattended—a foreign policy. Before it applies for statehood, what work does it actually have to do?

Three jobs, as far as I can tell.

In the first article, I argued that a specification earns its keep only when it makes software easier to change. That gives me a way to judge the result, but not a method. A six-page document can be useless. So can a six-line one. A pristine specification can describe last month’s system perfectly while today’s code wanders off unsupervised.

Checking whether the code works is not enough. Tests and retries may eventually produce a valid implementation even when the specification provides poor guidance; the result does not tell me how costly the path was or whether the decisions needed for the next change were preserved.

The useful question is not whether a team “does specification-driven development.” It is what knowledge the specification preserves, when that knowledge appears in front of the person or agent making a decision, and what happens when implementation teaches me something the specification did not know.

Compactly:

Compress intent, allocate context, reconcile discoveries.

These are not three stages in a new ceremony. They are three needs that a useful specification must balance.

A specification compresses intent

Code contains more than intent. It contains framework mechanics, library constraints, arbitrary choices made where the requirements were silent, temporary workarounds, and the occasional mistake that survived long enough to acquire seniority.

Suppose a subscription can be cancelled at any time. The user keeps access until the end of the period already paid for, and the subscription must not renew.

That is durable intent. An implementation may express it with a status enum, a scheduled job, a payment-provider flag, or a small creature living in a message queue. Those choices matter while building the feature, but they are not automatically part of what the feature means.

A plan records how I intend to make the change. A specification records what must remain true after the change is complete.

The specification should preserve the decisions that must survive a rewrite while leaving the rest fluid. Too little detail and two implementations can behave differently where only one behavior is acceptable. Too much detail and the specification becomes a sprawling prose copy of the code: all the implementation detail to maintain, with none of the tooling that makes code manageable.

The flexibility a specification permits tells me what it has compressed. Names and algorithms may vary without changing behavior. But if one cancelled subscription loses access immediately while another retains it until the paid period ends, either one implementation violates the specification or the specification has left a product decision unresolved.

So compression and sufficiency pull in opposite directions. Shrink the specification far enough and intent disappears. Expand it until every implementation choice is fixed and the specification preserves the very accidents it was meant to leave behind.

The target is neither endpoint:

Preserve the smallest model that rules out unacceptable differences in behavior without freezing incidental implementation choices.

The complete specification is usually the wrong context

Once useful intent has been preserved, another problem appears: which part belongs in front of the agent now?

The whole specification may be the complete description of the system and still be the wrong context for one decision. If I am changing the cancellation button, I probably need the access and renewal rules. If I am correcting a typo on the home page, I do not. A document can be worth keeping without being worth loading.

This is where “use less context” becomes dangerously easy advice. The smallest opening prompt is not necessarily the cheapest way to finish the task. Omit a rule and the agent may search for it, ask for clarification, choose a default, discover the mistake during review, and rewrite the feature. The prompt was tiny. The work was not.

The cost belongs to the whole path to a valid result:

Six labeled rectangular cards add initial context, retrieved context, corrections, retries, review, and repair into the total cost of reaching a valid result. Six labeled rectangular cards add initial context, retrieved context, corrections, retries, review, and repair into the total cost of reaching a valid result.
Opening context is only one part of the cost; retrieval, correction, retries, review, and repair count too.

That gives me a stronger rule than “load fewer tokens”:

Prefer the least costly context that produces valid work reliably.

Token count is part of that cost, but it does not tell me whether the context was useful. Better signs of missing or harmful context are repeated searches, late discovery of a constraint, decisions that have to be reversed, unsupported assumptions, and repairs that should not have been necessary.

“Complete” is therefore relative to the decision, not the project. One task may need rules from several parts of the system. One rule may govern dozens of tasks. A neat document hierarchy will not always match that many-to-many relationship.

Sometimes the simplest selector is no selector at all. If the specification is small enough, loading the whole thing may cost less than dividing it; context allocation becomes a problem only when selection is cheaper than carrying everything.

This is also where elaborate context architecture can become its own hobby. Before inventing a selector, metadata language, dependency graph, and ceremonial hat for the dependency graph, I would learn how real tasks use context and only then decide how to organize it.

Implementation is allowed to teach the specification

A specification that never changes will eventually become wrong. A specification that absorbs every detail from the code will eventually become the code.

Reconciliation is the path between those failures.

When the specification is right, fix the implementation

If cancellation removes access immediately despite the promised paid period, the implementation is wrong. Updating the specification would merely legitimize the defect.

When the product decision changes, update the specification

If implementation reveals that the selected payment integration can only cancel immediately with a prorated refund, and the product accepts that tradeoff, the specification should record the new behavior before the implementation adopts it.

Most implementation discoveries should remain implementation details. Reconciliation is not copying code into Markdown; it is deciding whether an implementation discovery should constrain future implementations.

Too little reconciliation produces drift: the next agent repeats an old mistake because the accepted knowledge never reached the durable intent. Too much produces bloat: one implementation’s field names, workarounds, and architecture become permanent law.

A good reconciliation process therefore has both recall and restraint. It keeps discoveries that matter to future decisions and rejects details that only explain the current implementation.

Three panels show several source documents compressed into one durable specification, selected rules copied from a larger specification into the current task, and an implementation discovery routed either into the specification or kept in the implementation. Three panels show several source documents compressed into one durable specification, selected rules copied from a larger specification into the current task, and an implementation discovery routed either into the specification or kept in the implementation.
Compression preserves the promise. Context allocation gives the task only what it needs. Reconciliation keeps the discovery and drops the debris.

Current workflows divide the three jobs differently

Two prominent current specification-driven development (SDD) workflows make useful examples: GitHub Spec Kit and OpenSpec. Their mapping to the three jobs is loose. Neither workflow uses these categories, and one artifact may serve more than one job.

GitHub Spec Kit separates the what and why in spec.md from the technical plan and dependency-ordered tasks. Clarification, requirements checklists, cross-artifact analysis, and convergence checks help preserve and verify intent. Task decomposition and staged implementation provide smaller units of working context.

Reconciliation is left to the team. Spec Kit documents a flow-back spec, a flow-forward spec, and a living spec, each with different rules for how specifications, plans, tasks, and implementation inform one another over time.

OpenSpec keeps the current specification separate from proposed changes. Delta specifications compress a change instead of repeating the whole system, while proposals, designs, and tasks organize context around one unit of work.

OpenSpec makes reconciliation more explicit. Artifacts remain editable during implementation, and archiving folds accepted delta specifications back into the current truth.

Compression and reconciliation have fairly visible homes in both workflows. Context allocation is less direct: decomposition, deltas, and change boundaries may help, but only actual use can reveal whether a particular task receives the least costly context.

The three jobs constrain one another

Optimizing one dimension alone breaks the others. Aggressive compression creates missing context. Loading everything hides a good specification inside irrelevant detail. Indiscriminate reconciliation destroys compression. Refusing to reconcile makes careful context allocation serve stale intent.

That is why I would not judge a specification by its length, completeness, or mere existence. I would ask three questions:

  1. Does it preserve the decisions whose loss would permit unacceptable behavior?
  2. Can each task get the decisions it needs without inviting the whole specification along?
  3. Does it learn durable facts from implementation without preserving implementation by accident?

This is not a recipe for the ideal specification. It is a way of thinking about the tradeoffs that make one useful. The balance depends on the system, the task, and the cost of being wrong.

The first article gave the specification one job: make software easier to change. This one gives that job a shape.

Keep the intent worth preserving, load it when the decision needs it, and let implementation teach the specification only what the next implementation should still know.

END OF FILE
user@inkspell:~/blog $
$ cd ..
phone down  ·  breathe  ·  begin
☸︎
ai ☸︎ agents ☸︎ specification

What a Useful Specification Has to Do

☸︎

A useful specification must compress intent, supply the right context for the current decision, and learn from code without becoming a copy of it.

session · 9 min sitting · July 30, 2026
☸︎

The modern specification has acquired a constitution, a workflow, several committees, and—if left unattended—a foreign policy. Before it applies for statehood, what work does it actually have to do?

— enter the practice —

Three jobs, as far as I can tell.

In the first article, I argued that a specification earns its keep only when it makes software easier to change. That gives me a way to judge the result, but not a method. A six-page document can be useless. So can a six-line one. A pristine specification can describe last month’s system perfectly while today’s code wanders off unsupervised.

Checking whether the code works is not enough. Tests and retries may eventually produce a valid implementation even when the specification provides poor guidance; the result does not tell me how costly the path was or whether the decisions needed for the next change were preserved.

The useful question is not whether a team “does specification-driven development.” It is what knowledge the specification preserves, when that knowledge appears in front of the person or agent making a decision, and what happens when implementation teaches me something the specification did not know.

Compactly:

Compress intent, allocate context, reconcile discoveries.

These are not three stages in a new ceremony. They are three needs that a useful specification must balance.

A specification compresses intent

Code contains more than intent. It contains framework mechanics, library constraints, arbitrary choices made where the requirements were silent, temporary workarounds, and the occasional mistake that survived long enough to acquire seniority.

Suppose a subscription can be cancelled at any time. The user keeps access until the end of the period already paid for, and the subscription must not renew.

That is durable intent. An implementation may express it with a status enum, a scheduled job, a payment-provider flag, or a small creature living in a message queue. Those choices matter while building the feature, but they are not automatically part of what the feature means.

A plan records how I intend to make the change. A specification records what must remain true after the change is complete.

The specification should preserve the decisions that must survive a rewrite while leaving the rest fluid. Too little detail and two implementations can behave differently where only one behavior is acceptable. Too much detail and the specification becomes a sprawling prose copy of the code: all the implementation detail to maintain, with none of the tooling that makes code manageable.

The flexibility a specification permits tells me what it has compressed. Names and algorithms may vary without changing behavior. But if one cancelled subscription loses access immediately while another retains it until the paid period ends, either one implementation violates the specification or the specification has left a product decision unresolved.

So compression and sufficiency pull in opposite directions. Shrink the specification far enough and intent disappears. Expand it until every implementation choice is fixed and the specification preserves the very accidents it was meant to leave behind.

The target is neither endpoint:

Preserve the smallest model that rules out unacceptable differences in behavior without freezing incidental implementation choices.

The complete specification is usually the wrong context

Once useful intent has been preserved, another problem appears: which part belongs in front of the agent now?

The whole specification may be the complete description of the system and still be the wrong context for one decision. If I am changing the cancellation button, I probably need the access and renewal rules. If I am correcting a typo on the home page, I do not. A document can be worth keeping without being worth loading.

This is where “use less context” becomes dangerously easy advice. The smallest opening prompt is not necessarily the cheapest way to finish the task. Omit a rule and the agent may search for it, ask for clarification, choose a default, discover the mistake during review, and rewrite the feature. The prompt was tiny. The work was not.

The cost belongs to the whole path to a valid result:

Six labeled rectangular cards add initial context, retrieved context, corrections, retries, review, and repair into the total cost of reaching a valid result. Six labeled rectangular cards add initial context, retrieved context, corrections, retries, review, and repair into the total cost of reaching a valid result.
Opening context is only one part of the cost; retrieval, correction, retries, review, and repair count too.

That gives me a stronger rule than “load fewer tokens”:

Prefer the least costly context that produces valid work reliably.

Token count is part of that cost, but it does not tell me whether the context was useful. Better signs of missing or harmful context are repeated searches, late discovery of a constraint, decisions that have to be reversed, unsupported assumptions, and repairs that should not have been necessary.

“Complete” is therefore relative to the decision, not the project. One task may need rules from several parts of the system. One rule may govern dozens of tasks. A neat document hierarchy will not always match that many-to-many relationship.

Sometimes the simplest selector is no selector at all. If the specification is small enough, loading the whole thing may cost less than dividing it; context allocation becomes a problem only when selection is cheaper than carrying everything.

This is also where elaborate context architecture can become its own hobby. Before inventing a selector, metadata language, dependency graph, and ceremonial hat for the dependency graph, I would learn how real tasks use context and only then decide how to organize it.

Implementation is allowed to teach the specification

A specification that never changes will eventually become wrong. A specification that absorbs every detail from the code will eventually become the code.

Reconciliation is the path between those failures.

When the specification is right, fix the implementation

If cancellation removes access immediately despite the promised paid period, the implementation is wrong. Updating the specification would merely legitimize the defect.

When the product decision changes, update the specification

If implementation reveals that the selected payment integration can only cancel immediately with a prorated refund, and the product accepts that tradeoff, the specification should record the new behavior before the implementation adopts it.

Most implementation discoveries should remain implementation details. Reconciliation is not copying code into Markdown; it is deciding whether an implementation discovery should constrain future implementations.

Too little reconciliation produces drift: the next agent repeats an old mistake because the accepted knowledge never reached the durable intent. Too much produces bloat: one implementation’s field names, workarounds, and architecture become permanent law.

A good reconciliation process therefore has both recall and restraint. It keeps discoveries that matter to future decisions and rejects details that only explain the current implementation.

Three panels show several source documents compressed into one durable specification, selected rules copied from a larger specification into the current task, and an implementation discovery routed either into the specification or kept in the implementation. Three panels show several source documents compressed into one durable specification, selected rules copied from a larger specification into the current task, and an implementation discovery routed either into the specification or kept in the implementation.
Compression preserves the promise. Context allocation gives the task only what it needs. Reconciliation keeps the discovery and drops the debris.

Current workflows divide the three jobs differently

Two prominent current specification-driven development (SDD) workflows make useful examples: GitHub Spec Kit and OpenSpec. Their mapping to the three jobs is loose. Neither workflow uses these categories, and one artifact may serve more than one job.

GitHub Spec Kit separates the what and why in spec.md from the technical plan and dependency-ordered tasks. Clarification, requirements checklists, cross-artifact analysis, and convergence checks help preserve and verify intent. Task decomposition and staged implementation provide smaller units of working context.

Reconciliation is left to the team. Spec Kit documents a flow-back spec, a flow-forward spec, and a living spec, each with different rules for how specifications, plans, tasks, and implementation inform one another over time.

OpenSpec keeps the current specification separate from proposed changes. Delta specifications compress a change instead of repeating the whole system, while proposals, designs, and tasks organize context around one unit of work.

OpenSpec makes reconciliation more explicit. Artifacts remain editable during implementation, and archiving folds accepted delta specifications back into the current truth.

Compression and reconciliation have fairly visible homes in both workflows. Context allocation is less direct: decomposition, deltas, and change boundaries may help, but only actual use can reveal whether a particular task receives the least costly context.

The three jobs constrain one another

Optimizing one dimension alone breaks the others. Aggressive compression creates missing context. Loading everything hides a good specification inside irrelevant detail. Indiscriminate reconciliation destroys compression. Refusing to reconcile makes careful context allocation serve stale intent.

That is why I would not judge a specification by its length, completeness, or mere existence. I would ask three questions:

  1. Does it preserve the decisions whose loss would permit unacceptable behavior?
  2. Can each task get the decisions it needs without inviting the whole specification along?
  3. Does it learn durable facts from implementation without preserving implementation by accident?

This is not a recipe for the ideal specification. It is a way of thinking about the tradeoffs that make one useful. The balance depends on the system, the task, and the cost of being wrong.

The first article gave the specification one job: make software easier to change. This one gives that job a shape.

Keep the intent worth preserving, load it when the decision needs it, and let implementation teach the specification only what the next implementation should still know.

☸︎
the session ends
learn  ·  unlearn  ·  return
return when ready
☠︎ LARTS-SERVER BBS v2.3.1 — root console
⚠︎ Unauthorised access will be met with creative, legally ambiguous, and frankly disproportionate solutions.
Last login: 2026-07-30 00:00:00 from somewhere you'll regret  ·  session pid 1396
⚡ luser activity is being monitored. it always was. 9 LARTs administered today.
☠︎ BOFH EXCUSE #1150 :: an intern touched the production server (RIP)
root@larts-server:/var/rants # cat what-a-useful-specification-must-do.txt  # and what was your username again?
SUBJECT: What a Useful Specification Has to Do
DATE: 2026-07-30 00:00:00 READ: ~9 min of your billable downtime WORDS: 1623
TAGS: [ai] [agents] [specification]
// A useful specification must compress intent, supply the right context for the current decision, and learn from code without becoming a copy of it.
☠︎ ::: BEGIN TRANSMISSION — touch nothing ::: ☠︎
⚠︎ PRIORITY MESSAGE FROM THE OPERATOR ⚠︎

The modern specification has acquired a constitution, a workflow, several committees, and—if left unattended—a foreign policy. Before it applies for statehood, what work does it actually have to do?

Three jobs, as far as I can tell.

In the first article, I argued that a specification earns its keep only when it makes software easier to change. That gives me a way to judge the result, but not a method. A six-page document can be useless. So can a six-line one. A pristine specification can describe last month’s system perfectly while today’s code wanders off unsupervised.

Checking whether the code works is not enough. Tests and retries may eventually produce a valid implementation even when the specification provides poor guidance; the result does not tell me how costly the path was or whether the decisions needed for the next change were preserved.

The useful question is not whether a team “does specification-driven development.” It is what knowledge the specification preserves, when that knowledge appears in front of the person or agent making a decision, and what happens when implementation teaches me something the specification did not know.

Compactly:

Compress intent, allocate context, reconcile discoveries.

These are not three stages in a new ceremony. They are three needs that a useful specification must balance.

A specification compresses intent

Code contains more than intent. It contains framework mechanics, library constraints, arbitrary choices made where the requirements were silent, temporary workarounds, and the occasional mistake that survived long enough to acquire seniority.

Suppose a subscription can be cancelled at any time. The user keeps access until the end of the period already paid for, and the subscription must not renew.

That is durable intent. An implementation may express it with a status enum, a scheduled job, a payment-provider flag, or a small creature living in a message queue. Those choices matter while building the feature, but they are not automatically part of what the feature means.

A plan records how I intend to make the change. A specification records what must remain true after the change is complete.

The specification should preserve the decisions that must survive a rewrite while leaving the rest fluid. Too little detail and two implementations can behave differently where only one behavior is acceptable. Too much detail and the specification becomes a sprawling prose copy of the code: all the implementation detail to maintain, with none of the tooling that makes code manageable.

The flexibility a specification permits tells me what it has compressed. Names and algorithms may vary without changing behavior. But if one cancelled subscription loses access immediately while another retains it until the paid period ends, either one implementation violates the specification or the specification has left a product decision unresolved.

So compression and sufficiency pull in opposite directions. Shrink the specification far enough and intent disappears. Expand it until every implementation choice is fixed and the specification preserves the very accidents it was meant to leave behind.

The target is neither endpoint:

Preserve the smallest model that rules out unacceptable differences in behavior without freezing incidental implementation choices.

The complete specification is usually the wrong context

Once useful intent has been preserved, another problem appears: which part belongs in front of the agent now?

The whole specification may be the complete description of the system and still be the wrong context for one decision. If I am changing the cancellation button, I probably need the access and renewal rules. If I am correcting a typo on the home page, I do not. A document can be worth keeping without being worth loading.

This is where “use less context” becomes dangerously easy advice. The smallest opening prompt is not necessarily the cheapest way to finish the task. Omit a rule and the agent may search for it, ask for clarification, choose a default, discover the mistake during review, and rewrite the feature. The prompt was tiny. The work was not.

The cost belongs to the whole path to a valid result:

Six labeled rectangular cards add initial context, retrieved context, corrections, retries, review, and repair into the total cost of reaching a valid result. Six labeled rectangular cards add initial context, retrieved context, corrections, retries, review, and repair into the total cost of reaching a valid result.
Opening context is only one part of the cost; retrieval, correction, retries, review, and repair count too.

That gives me a stronger rule than “load fewer tokens”:

Prefer the least costly context that produces valid work reliably.

Token count is part of that cost, but it does not tell me whether the context was useful. Better signs of missing or harmful context are repeated searches, late discovery of a constraint, decisions that have to be reversed, unsupported assumptions, and repairs that should not have been necessary.

“Complete” is therefore relative to the decision, not the project. One task may need rules from several parts of the system. One rule may govern dozens of tasks. A neat document hierarchy will not always match that many-to-many relationship.

Sometimes the simplest selector is no selector at all. If the specification is small enough, loading the whole thing may cost less than dividing it; context allocation becomes a problem only when selection is cheaper than carrying everything.

This is also where elaborate context architecture can become its own hobby. Before inventing a selector, metadata language, dependency graph, and ceremonial hat for the dependency graph, I would learn how real tasks use context and only then decide how to organize it.

Implementation is allowed to teach the specification

A specification that never changes will eventually become wrong. A specification that absorbs every detail from the code will eventually become the code.

Reconciliation is the path between those failures.

When the specification is right, fix the implementation

If cancellation removes access immediately despite the promised paid period, the implementation is wrong. Updating the specification would merely legitimize the defect.

When the product decision changes, update the specification

If implementation reveals that the selected payment integration can only cancel immediately with a prorated refund, and the product accepts that tradeoff, the specification should record the new behavior before the implementation adopts it.

Most implementation discoveries should remain implementation details. Reconciliation is not copying code into Markdown; it is deciding whether an implementation discovery should constrain future implementations.

Too little reconciliation produces drift: the next agent repeats an old mistake because the accepted knowledge never reached the durable intent. Too much produces bloat: one implementation’s field names, workarounds, and architecture become permanent law.

A good reconciliation process therefore has both recall and restraint. It keeps discoveries that matter to future decisions and rejects details that only explain the current implementation.

Three panels show several source documents compressed into one durable specification, selected rules copied from a larger specification into the current task, and an implementation discovery routed either into the specification or kept in the implementation. Three panels show several source documents compressed into one durable specification, selected rules copied from a larger specification into the current task, and an implementation discovery routed either into the specification or kept in the implementation.
Compression preserves the promise. Context allocation gives the task only what it needs. Reconciliation keeps the discovery and drops the debris.

Current workflows divide the three jobs differently

Two prominent current specification-driven development (SDD) workflows make useful examples: GitHub Spec Kit and OpenSpec. Their mapping to the three jobs is loose. Neither workflow uses these categories, and one artifact may serve more than one job.

GitHub Spec Kit separates the what and why in spec.md from the technical plan and dependency-ordered tasks. Clarification, requirements checklists, cross-artifact analysis, and convergence checks help preserve and verify intent. Task decomposition and staged implementation provide smaller units of working context.

Reconciliation is left to the team. Spec Kit documents a flow-back spec, a flow-forward spec, and a living spec, each with different rules for how specifications, plans, tasks, and implementation inform one another over time.

OpenSpec keeps the current specification separate from proposed changes. Delta specifications compress a change instead of repeating the whole system, while proposals, designs, and tasks organize context around one unit of work.

OpenSpec makes reconciliation more explicit. Artifacts remain editable during implementation, and archiving folds accepted delta specifications back into the current truth.

Compression and reconciliation have fairly visible homes in both workflows. Context allocation is less direct: decomposition, deltas, and change boundaries may help, but only actual use can reveal whether a particular task receives the least costly context.

The three jobs constrain one another

Optimizing one dimension alone breaks the others. Aggressive compression creates missing context. Loading everything hides a good specification inside irrelevant detail. Indiscriminate reconciliation destroys compression. Refusing to reconcile makes careful context allocation serve stale intent.

That is why I would not judge a specification by its length, completeness, or mere existence. I would ask three questions:

  1. Does it preserve the decisions whose loss would permit unacceptable behavior?
  2. Can each task get the decisions it needs without inviting the whole specification along?
  3. Does it learn durable facts from implementation without preserving implementation by accident?

This is not a recipe for the ideal specification. It is a way of thinking about the tradeoffs that make one useful. The balance depends on the system, the task, and the cost of being wrong.

The first article gave the specification one job: make software easier to change. This one gives that job a shape.

Keep the intent worth preserving, load it when the decision needs it, and let implementation teach the specification only what the next implementation should still know.

☠︎ ::: END OF FILE — this never happened ::: ☠︎
[2026-07-30 00:00:00] SYSTEM: memory consumed by luser requests: 97.3%
[2026-07-30 00:00:00] SYSTEM: your read has been logged against your permanent record.
root@larts-server:/var/rants # logout  # don't let the airlock hit you
⛤︎
the circle is opened
☾ return to the archive ☽
☾ ai ☽ ☾ agents ☽ ☾ specification ☽
⛤︎   FATUM   ⛤︎

What a Useful Specification Has to Do

☾   ⛤︎   ☽

A useful specification must compress intent, supply the right context for the current decision, and learn from code without becoming a copy of it.

July 30, 2026 ⛤︎ 9 min rite ⛤︎ 1623 words
☾   ⛤︎   ☽

The modern specification has acquired a constitution, a workflow, several committees, and—if left unattended—a foreign policy. Before it applies for statehood, what work does it actually have to do?

Three jobs, as far as I can tell.

In the first article, I argued that a specification earns its keep only when it makes software easier to change. That gives me a way to judge the result, but not a method. A six-page document can be useless. So can a six-line one. A pristine specification can describe last month’s system perfectly while today’s code wanders off unsupervised.

Checking whether the code works is not enough. Tests and retries may eventually produce a valid implementation even when the specification provides poor guidance; the result does not tell me how costly the path was or whether the decisions needed for the next change were preserved.

The useful question is not whether a team “does specification-driven development.” It is what knowledge the specification preserves, when that knowledge appears in front of the person or agent making a decision, and what happens when implementation teaches me something the specification did not know.

Compactly:

Compress intent, allocate context, reconcile discoveries.

These are not three stages in a new ceremony. They are three needs that a useful specification must balance.

A specification compresses intent

Code contains more than intent. It contains framework mechanics, library constraints, arbitrary choices made where the requirements were silent, temporary workarounds, and the occasional mistake that survived long enough to acquire seniority.

Suppose a subscription can be cancelled at any time. The user keeps access until the end of the period already paid for, and the subscription must not renew.

That is durable intent. An implementation may express it with a status enum, a scheduled job, a payment-provider flag, or a small creature living in a message queue. Those choices matter while building the feature, but they are not automatically part of what the feature means.

A plan records how I intend to make the change. A specification records what must remain true after the change is complete.

The specification should preserve the decisions that must survive a rewrite while leaving the rest fluid. Too little detail and two implementations can behave differently where only one behavior is acceptable. Too much detail and the specification becomes a sprawling prose copy of the code: all the implementation detail to maintain, with none of the tooling that makes code manageable.

The flexibility a specification permits tells me what it has compressed. Names and algorithms may vary without changing behavior. But if one cancelled subscription loses access immediately while another retains it until the paid period ends, either one implementation violates the specification or the specification has left a product decision unresolved.

So compression and sufficiency pull in opposite directions. Shrink the specification far enough and intent disappears. Expand it until every implementation choice is fixed and the specification preserves the very accidents it was meant to leave behind.

The target is neither endpoint:

Preserve the smallest model that rules out unacceptable differences in behavior without freezing incidental implementation choices.

The complete specification is usually the wrong context

Once useful intent has been preserved, another problem appears: which part belongs in front of the agent now?

The whole specification may be the complete description of the system and still be the wrong context for one decision. If I am changing the cancellation button, I probably need the access and renewal rules. If I am correcting a typo on the home page, I do not. A document can be worth keeping without being worth loading.

This is where “use less context” becomes dangerously easy advice. The smallest opening prompt is not necessarily the cheapest way to finish the task. Omit a rule and the agent may search for it, ask for clarification, choose a default, discover the mistake during review, and rewrite the feature. The prompt was tiny. The work was not.

The cost belongs to the whole path to a valid result:

Six labeled rectangular cards add initial context, retrieved context, corrections, retries, review, and repair into the total cost of reaching a valid result. Six labeled rectangular cards add initial context, retrieved context, corrections, retries, review, and repair into the total cost of reaching a valid result.
Opening context is only one part of the cost; retrieval, correction, retries, review, and repair count too.

That gives me a stronger rule than “load fewer tokens”:

Prefer the least costly context that produces valid work reliably.

Token count is part of that cost, but it does not tell me whether the context was useful. Better signs of missing or harmful context are repeated searches, late discovery of a constraint, decisions that have to be reversed, unsupported assumptions, and repairs that should not have been necessary.

“Complete” is therefore relative to the decision, not the project. One task may need rules from several parts of the system. One rule may govern dozens of tasks. A neat document hierarchy will not always match that many-to-many relationship.

Sometimes the simplest selector is no selector at all. If the specification is small enough, loading the whole thing may cost less than dividing it; context allocation becomes a problem only when selection is cheaper than carrying everything.

This is also where elaborate context architecture can become its own hobby. Before inventing a selector, metadata language, dependency graph, and ceremonial hat for the dependency graph, I would learn how real tasks use context and only then decide how to organize it.

Implementation is allowed to teach the specification

A specification that never changes will eventually become wrong. A specification that absorbs every detail from the code will eventually become the code.

Reconciliation is the path between those failures.

When the specification is right, fix the implementation

If cancellation removes access immediately despite the promised paid period, the implementation is wrong. Updating the specification would merely legitimize the defect.

When the product decision changes, update the specification

If implementation reveals that the selected payment integration can only cancel immediately with a prorated refund, and the product accepts that tradeoff, the specification should record the new behavior before the implementation adopts it.

Most implementation discoveries should remain implementation details. Reconciliation is not copying code into Markdown; it is deciding whether an implementation discovery should constrain future implementations.

Too little reconciliation produces drift: the next agent repeats an old mistake because the accepted knowledge never reached the durable intent. Too much produces bloat: one implementation’s field names, workarounds, and architecture become permanent law.

A good reconciliation process therefore has both recall and restraint. It keeps discoveries that matter to future decisions and rejects details that only explain the current implementation.

Three panels show several source documents compressed into one durable specification, selected rules copied from a larger specification into the current task, and an implementation discovery routed either into the specification or kept in the implementation. Three panels show several source documents compressed into one durable specification, selected rules copied from a larger specification into the current task, and an implementation discovery routed either into the specification or kept in the implementation.
Compression preserves the promise. Context allocation gives the task only what it needs. Reconciliation keeps the discovery and drops the debris.

Current workflows divide the three jobs differently

Two prominent current specification-driven development (SDD) workflows make useful examples: GitHub Spec Kit and OpenSpec. Their mapping to the three jobs is loose. Neither workflow uses these categories, and one artifact may serve more than one job.

GitHub Spec Kit separates the what and why in spec.md from the technical plan and dependency-ordered tasks. Clarification, requirements checklists, cross-artifact analysis, and convergence checks help preserve and verify intent. Task decomposition and staged implementation provide smaller units of working context.

Reconciliation is left to the team. Spec Kit documents a flow-back spec, a flow-forward spec, and a living spec, each with different rules for how specifications, plans, tasks, and implementation inform one another over time.

OpenSpec keeps the current specification separate from proposed changes. Delta specifications compress a change instead of repeating the whole system, while proposals, designs, and tasks organize context around one unit of work.

OpenSpec makes reconciliation more explicit. Artifacts remain editable during implementation, and archiving folds accepted delta specifications back into the current truth.

Compression and reconciliation have fairly visible homes in both workflows. Context allocation is less direct: decomposition, deltas, and change boundaries may help, but only actual use can reveal whether a particular task receives the least costly context.

The three jobs constrain one another

Optimizing one dimension alone breaks the others. Aggressive compression creates missing context. Loading everything hides a good specification inside irrelevant detail. Indiscriminate reconciliation destroys compression. Refusing to reconcile makes careful context allocation serve stale intent.

That is why I would not judge a specification by its length, completeness, or mere existence. I would ask three questions:

  1. Does it preserve the decisions whose loss would permit unacceptable behavior?
  2. Can each task get the decisions it needs without inviting the whole specification along?
  3. Does it learn durable facts from implementation without preserving implementation by accident?

This is not a recipe for the ideal specification. It is a way of thinking about the tradeoffs that make one useful. The balance depends on the system, the task, and the cost of being wrong.

The first article gave the specification one job: make software easier to change. This one gives that job a shape.

Keep the intent worth preserving, load it when the decision needs it, and let implementation teach the specification only what the next implementation should still know.

☾  ⛤︎  ────────  ⛤︎  ☽
so it is written · so it is bound
☾ return to the archive ☽
inkspell
© 2026 inkspell RSS