Closing the Gap — Brownfield Toward Greenfield
Moving an existing codebase toward full spec coverage, what that can and cannot honestly mean, the six-stage path, and the one place deliberate back-filling is justified.
"Get us from brownfield to greenfield" is a reasonable thing to want and an ambiguous thing to ask for. This page starts by pinning down what it means, because the obvious reading is one the tool deliberately refuses — and saying otherwise would be selling you something that does not exist.
8.1 What it can and cannot mean
FIGURE A8 — "Get us from brownfield to greenfield" — what that can honestly mean. The obvious reading is on the left. The reachable one is on the right.
What it cannot mean is one pass that converts 80,000 lines into a complete specification set. You schedule a project, somebody writes specifications for every module, and three months later openspec/specs/ describes the whole system.
There is no command that does this, and the absence is deliberate. Nothing forces a back-filled specification to track reality: it was not produced by a change, so no change updates it. Within two releases parts of it are wrong — and once one part is known to be wrong, the accurate parts stop being trusted too. You end up with less than you started with.
This is not a reading of the tool. It is what OpenSpec's own documentation says about itself:
"Resist the urge to back-fill everything. Writing specs for code you aren't changing feels productive and usually isn't. Those specs go stale, because nothing forces them to track reality."
and
"Forcing a one-time bulk conversion tends to produce a large, stale spec nobody trusts."
What it can mean, and does, is this: every domain you actively work in is specification-covered, so all new work in those areas proceeds specification-first. That end state is genuinely reachable, every line of it is produced by a real change, and every line is kept true by the next change. An area that goes stale is an area nobody is working in — and you find out, because the next change there fails validation rather than passing quietly against an outdated requirement.
Avoid: promising a stakeholder "we will be fully specified by Q3." That is the left-hand card, and it is a commitment to produce a document rather than to reach a state. The honest version — "every area we touch is specified, and here is the coverage map" — is defensible and this page is how you get there.
If what you actually need is a document describing the current system for an audit or a handover, that is a real requirement and OpenSpec is the wrong instrument for it. Write that document, keep it where it lives, and do not confuse it with a specification kept true by work.
8.2 The path
FIGURE A9 — The road from no specifications to spec-covered where it counts. Four of these six stages are just doing your job. Only stages one, four and five are anything you would call adoption work.
Stage 1 — set the context once. openspec init, then fill in config.yaml's context: with your stack and conventions. Every artifact anyone generates from here on inherits it. This is an hour, once.
Stage 2 — one supervised cycle. /opsx:onboard on a real, small task, with the expanded profile enabled. Fifteen to twenty minutes, and it leaves you with a genuine change you can keep or discard.
Stage 3 — then simply do the work. Ordinary changes, repeatedly. Every archive merges one delta into openspec/specs/, so coverage grows as a by-product of work you had to do anyway. This is the engine, and it is not adoption work at all.
Stage 4 — reconcile without finishing. /opsx:sync pushes a change's deltas into the main specifications without archiving the change. It exists for long-running work where the specifications need to be true before the change is complete.
Two properties of sync are worth knowing because they are easy to assume wrongly. It is agent-driven rather than a mechanical copy — the assistant reads both the delta and the main specification and merges intelligently, so it can add one new scenario without rewriting the requirement around it. And it merges rather than overwrites: anything the delta does not mention is preserved, in the main specification's existing order. It ends by running openspec validate --specs and will not claim success if that fails.
Stage 5 — keep the baseline honest. openspec validate --specs --strict, in continuous integration. The growing specification set stays well-formed rather than quietly rotting as it accumulates. This is the cheapest stage of all six and the one most often skipped.
Stage 6 — see where you actually are. openspec view, openspec list --specs, openspec spec list --long.
8.3 How you know a domain is done
Per domain, never per repository. A domain has arrived when three things are true:
- Every requirement in it has at least one scenario.
openspec validate --specs --strictis clean for it.- The last three changes in that area needed no new capability — meaning the shape of the domain has stopped moving.
Frame the result as a coverage map, not a percentage. "Ninety per cent specified" is not a claim this method can make, because there is no denominator: the set of things that should be specified is exactly the set of things somebody is working on, and that set changes every sprint.
8.4 Where back-filling is actually justified
There is one honest exception to §7.1, and pretending otherwise would leave a real gap in your system unaddressed.
FIGURE A10 — Where coverage arrives on its own, and where it never will. Plot each domain by how often it changes and how much it matters if it is wrong. Only one box needs a decision from you.
Three of the four boxes need no decision. High-risk areas that change constantly — authentication, payments, the public API surface — get specified within weeks by ordinary work. Low-risk areas that change often get specified as a side effect, which is harmless. Low-risk areas nobody touches should be left alone, because writing specifications there costs real time and nothing will keep them true.
The top-right box is the exception, and it is the one place a targeted back-fill is genuinely justified. A licence check, a consent ledger, anything under audit that nobody has changed in two years. Accretion will never reach these, because no ordinary change ever touches them. If one of those areas matters enough, write a deliberate documentation change for it — on purpose, named, and only for that domain.
How to write one, as an ordinary change rather than a special procedure:
1 Create the change. Its proposal says the capability already exists
and is being written down, rather than being built.
2 Write the delta as ## ADDED Requirements describing current
behaviour — what the code does today, not what you wish it did.
3 Make tasks.md verification steps rather than build steps:
"confirm the licence check rejects an expired key", not
"implement the licence check".
4 Archive it exactly like any other change.And then accept the obligation that comes with it. You now own keeping that specification true, because no change will do it for you — which is precisely the property that makes the left-hand card of FIGURE A8 a bad idea at scale. One or two of these is a considered decision. Fifteen of them is the big-bang back-fill wearing a different hat.
Note — most teams have between one and three domains in the top-right box. If your list has fifteen, the list is wrong rather than the method: re-read the two axes and check whether "rarely touched" is really true of all of them.
8.5 The relationship to the brownfield page
Adopting it on an existing codebase and this page are the same road. The difference is only whether you are content to let coverage arrive on its own schedule.
If you are, stop after stage three and you have everything OpenSpec is designed to give you. If you are not — because an auditor is asking, or because a high-risk area genuinely never changes — stages four to six and the top-right box of FIGURE A10 are how you close the remaining distance without producing a document that rots.
How is this guide?
Last updated on
Starting a New Project With It
The greenfield case. There is no existing code to catch a wrong assumption, so the two guard rails carry more weight than they do anywhere else.
The Worked Example
One real automation, from a fuzzy idea to a change folder on disk — the publishing pipeline, its four settled questions, and the proposal that came out.