13 min de citit · Publicat 14 mai 2026 · Actualizat 28 august 2026
The OpenSpec Method in Production: What We Actually Do
Six months of shipping with OpenSpec as the operating layer: what the propose–apply–archive loop looks like when it runs on real products, what breaks, and what becomes invisible.
Pavel Rapoport · În engleză
«The spec is not documentation. The spec is the source that generates the documentation, the plan, and the execution constraints — in that order.»
Until late 2025 the studio was running a hybrid: some changes had specs, most did not. The AI agents were capable but inconsistent. A change that touched the database schema might be executed precisely if the agent happened to have good context that day, and imprecisely the next time a different model version handled it with different context-window fill.
The shift to OpenSpec as a mandatory operating layer was not a grand declaration. It was a quiet resolution: nothing ships without a triplet.
What follows is the honest accounting of what that looks like in practice — the workflow, the failure modes we hit, the tradeoffs we still carry, and the things that have become so routine we no longer think about them.
The triplet in one sentence
Every change to the platform produces three files before any code runs:
- proposal.md — what problem this solves, why now, what is out of scope
- design.md — the technical decisions already made, with their alternatives and the reasoning for each choice
- tasks.md — a numbered execution sequence an AI agent can follow step by step
That is the entire ceremony. No ticketing system integration, no mandatory templates beyond a few headers, no approval gates except the one that matters: the human reads the design.md and says yes before the agent runs.
What happens inside a change
Phase 1: propose
The proposal phase is short. Ten to fifteen minutes of thinking, usually by the architect, sometimes by Pavel directly. The proposal answers three questions:
- What is broken or missing, and what evidence do we have that it is?
- What does the change do, stated precisely enough that an agent could understand scope?
- What is explicitly not in scope?
The "explicitly not in scope" section is the most valuable one. It is the place where the proposal rules out the obvious extensions that would bloat the change, and it is the place the agent reads first when it is tempted to "helpfully" add something adjacent.
A proposal for adding author attribution to lab posts is not also a proposal for redesigning the lab index card, even if those feel related. The explicit exclusion saves one cycle of rework.
Phase 2: design
The design file is where the hard work happens. Not implementation — decisions.
At the design phase, the question is not "how do we implement this?" It is "which of the feasible implementations will we use, and why?" Every decision in the design file names the alternatives that were considered and states which one was chosen with the reason.
This is the part of OpenSpec that feels most like overhead on first contact and most like investment at the second rework. When the change ships and a related change arrives three months later, the future implementer — human or AI — reads the design file and finds the decision already made, with the reasoning attached. The question "why did they use a flat loader instead of an ORM here?" has an answer in the archive.
The design file also surfaces open questions explicitly. A question that is unresolved at design time gets a Q-LABEL: a named open question that either gets resolved before tasks run or gets escalated to a human. The agent cannot proceed past an open question that affects its execution path; it surfaces the question and stops.
This is the mechanism that prevents the most common agent failure mode: the confident mistake. An agent that has an open question and no mechanism to surface it will guess. The OpenSpec Q-LABEL convention makes guessing impossible — it must either answer the question from context or ask.
Phase 3: archive
When a change ships — code merged, tests green, PR closed — the triplet moves to openspec/archive/. The capability spec files under openspec/current/ are updated to reflect the new state.
The archive is permanent. The capability spec is the living document; it gets updated on every change that touches it. The archive folder is the historical record; it never changes after archive.
This distinction matters because it lets the agent answer two different questions from two different sources:
- "What is the current contract for lab post frontmatter?" → read
openspec/current/web.md - "Why did we add the
topicfield to lab post frontmatter?" → readopenspec/archive/2026-04-12-add-topic-field/design.md
Both answers are available. Neither requires human memory.
Six patterns we have learned
1. The proposal is a scope contract, not a scope wish
Early proposals were too aspirational. They described what the change could do if the team had more time, and then implicitly expected the agent to narrow scope at execution time. The agent did not narrow scope — it tried to implement everything in the proposal, failed halfway, and produced a large broken diff.
The fix was to treat the proposal as a contract: if it is in the proposal, the agent will attempt it. If it is not in the proposal, the agent will not touch it. The discipline of writing a realistic proposal became the discipline of scoping realistically.
2. Design files need "not this" as much as "this"
A design file that only describes the chosen implementation leaves the agent without guardrails when it encounters an adjacent pattern. It will adopt whatever pattern it finds in the existing code, which may or may not match the design intention.
Design files that name the rejected alternatives explicitly — "we evaluated a GraphQL resolver approach and rejected it because X" — give the agent a frame of reference that survives the exploration of the codebase. When the agent encounters a GraphQL resolver elsewhere in the code, it knows that the current change does not use that pattern and should not introduce it.
3. Tasks must be executable in order
The tasks file is a numbered sequence. The agent executes them in order. Tasks that assume a prior task's output must appear after that task. Tasks that are order-independent should be ordered anyway, because parallelism in a task file introduces coordination complexity that agents handle poorly.
Early task files had items like "3. Update loader to handle new schema" appearing before "4. Migrate existing MDX files to new schema." The agent executing task 3 before task 4 wrote a loader against a schema that did not yet exist in the files, and the type-checker found it. The fix was to establish a convention: schema changes always before code that depends on schema; migrations always before loaders that consume migrated data.
4. Open questions that block execution must be resolved before dispatch
A task file with an unresolved open question that affects execution cannot be dispatched to an agent. The agent will surface the question, stop, and wait. This is correct behavior, but it means the wall-clock time for the change grows by however long it takes to get an answer.
The mitigation is to resolve open questions in the design review, not at execution time. The design review is synchronous and cheap; the agent waiting for a human response is asynchronous and expensive. Front-load the resolution.
5. Capability specs must be kept current
If openspec/current/web.md says the lab post frontmatter has five fields but the code has seven, the next agent to read the spec and the code will receive conflicting signals. Most of the time, the agent will defer to the code (it is observable) over the spec (it is text). But the spec is then useless — it is not the source of truth, it is a stale document.
Keeping the capability spec current means: every time a change adds, modifies, or removes a field in the frontmatter, the capability spec is updated in the same PR. Not the next PR. The same PR.
6. Archive folders are frozen after merge
Once a change is archived, its proposal, design, and tasks files are read-only. The archive is a historical record. If a decision made in a past change turns out to be wrong, the correction is a new change with a new triplet, not an edit to the archived triplet.
This is initially counterintuitive — why not fix the historical document? Because the agent that reads an archived triplet needs to know what was decided at the time the change shipped, not what was decided in retrospect. The archive is forensic evidence, not a living spec.
What OpenSpec does not solve
Cross-session context
OpenSpec solves the within-session context problem: the agent reads the spec at the start of its session and has the reasoning available throughout. It does not solve the cross-session memory problem: if two separate agent sessions are running concurrently on different changes, they do not share state. The design files for both changes live in the repository, and both agents can read both, but there is no automatic collision detection.
We handle this with a human review gate before dispatch: the architect scans the pending change queue before dispatching new agents to check for changes that touch the same files. When two changes touch the same files, they are dispatched serially, not in parallel.
Spec drift
A capability spec can drift from reality if changes ship without updating the spec. The only guard against this is discipline: every change that touches a capability must update the spec. We have CI hooks that check whether OpenSpec files changed in PRs that touch spec-adjacent code, but the check is heuristic — it can miss subtle capability changes.
Intent verification
The spec describes intent. It does not verify that the implementation matches intent. Tests do that. OpenSpec and tests are complementary, not substitutes. A change with a good spec and no tests is a guess that hasn't been validated; a change with good tests and no spec is a black box that works but cannot be extended safely.
The numbers from the last 90 days
Data as of 2026-05-14, covering the preceding 90 days. Not re-counted since; read the shape, not the absolute figures. "Change" here means one merged pull request that carried its own scope — which is a broader unit than an OpenSpec change folder, of which the archive holds 190 for the same window. The definition is doing real work in this number and belongs next to it.
In that window the studio shipped 247 changes. Of these:
- 241 had a full triplet (proposal + design + tasks) before dispatch
- 6 were hotfixes — defects found in production requiring a same-day patch. Four of these were too small for a full triplet (one-line fixes); two were medium changes that should have had triplets and did not.
- 0 changes required a full revert due to scope creep
- 14 changes required partial rework due to a task that was ambiguously scoped
- 3 changes required escalation of an open question that had been marked as "resolved at execution time" but turned out to be unresolvable without human input
The 14 reworks trace to proposals that included scope without explicit exclusions. The 3 escalations trace to design files that deferred Q-LABELs that should have been resolved before dispatch. Both are now standard review criteria in the design review gate.
Tooling
Everything above is the method, and it is tool-agnostic: three files, one folder per change, an archive that is never edited after the fact. It needs no particular CLI and no issue tracker.
What follows is the studio's own implementation of it, which is a different thing and moves faster. As of August 2026: the CLI is @fission-ai/openspec. The forge build command in this repository is the studio's orchestrator wrapping it — it reads the tasks.md, dispatches changes to Claude Code with the relevant spec context pre-injected, and handles the push/PR/merge lifecycle. Dispatch was keyed to Linear issues when this article was written; Linear is paused as of 2026-08-27 and changes are currently executed without an issue id. The method did not change when that happened, which is rather the point of keeping the two separable.
The Forge orchestrator is itself spec-driven: its own changes live in openspec/archive/. The thing that runs the methodology is built by the methodology. This is not a cute observation — it is the only reason the studio has high confidence in Forge's behavior on new change types. When Forge handles a new scenario, there is a spec that describes what it should do.
Where it goes from here
The next evolution is inter-spec dependency tracking: if a design in change A declares a dependency on a decision in change B, the merge order is enforced at the CI level, not by human memory. The studio is building this now.
The horizon after that is automated spec consistency checking: a CI job that reads the code and the capability specs and flags divergences. This is hard — verifying that code satisfies a spec requires encoding the spec's claims as machine-checkable assertions, which is the same problem that formal verification has been working on for decades — but LLM-assisted consistency checking is a plausible near-term approach.
Both of these are improvements on a methodology that already works. The core loop — propose, apply, archive — is not changing. What changes is the speed and reliability of the checks around it.
FAQ
What is the OpenSpec method?
OpenSpec is a spec-anchored development methodology. Every change to a product is described in a three-file triplet — proposal.md, design.md, tasks.md — before any code is written. AI agents read these files at runtime to understand what they are building and why. After the change ships, the triplet is archived and the capability spec is updated to reflect the new state.
How is OpenSpec different from writing a design doc?
A design doc is usually written once, read once, and then abandoned when it drifts from the code. OpenSpec changes are structured in a way that makes them machine-readable at task time: the agent reads proposal.md to understand intent, design.md to understand the technical decisions already made, and tasks.md for the specific work to execute. The archive creates a durable record that survives context-window turnover.
Does OpenSpec slow down delivery?
On simple tasks it adds a few minutes of structured thinking before an agent starts writing code. On complex tasks it pays back in the first iteration — the agent spends fewer cycles re-asking scope questions and produces fewer out-of-scope changes. At studio operating tempo (several parallel builds per day), the net effect is faster delivery: fewer rework cycles, fewer merge conflicts, fewer cases where one agent's work breaks another's.
What does the archive actually contain?
The archive contains every change triplet in date-prefixed folders under openspec/archive/. It is a permanent record of what was decided, why, and what alternatives were considered. When a future change re-enters the same area, the agent reads the archive to understand the reasoning behind the current code before proposing modifications.
Can OpenSpec be used without AI agents?
Yes. The methodology works with human developers reading the triplets directly. The spec-anchored approach is valuable independent of AI: it forces decisions to be made before implementation begins, creates a shared mental model across the team, and produces documentation that does not rot. AI agents amplify the value by being able to read and apply the specs mechanically.
Further reading
- Fission-AI/OpenSpec — the CLI and spec format
- Why OpenSpec, Not Spec Kit — the studio's comparison of spec-driven tools
- GitHub Spec Kit — the primary alternative for enterprise contexts
- Tessl — the radical bet on AI-as-compiler, where the spec is the artifact
Verificat parțial — cifrele au data măsurătorii