My OpenSpec Workflow: Spec-Driven Development With AI Agents
The OpenSpec workflow I use with AI agents: spec, adversarial plan review, isolated build, red-team code review, then verify. Real PR numbers inside.
Agents don’t fail at writing code. They fail at knowing what to build, and at noticing what they got wrong.
That one sentence explains the shape of my entire workflow. Most AI-coding content optimizes the middle — the typing — which is now the cheap part. After ten years of shipping real-world software and a year of building with agents daily, the effort goes where the failures are: before the code (specs, plan review) and after it (adversarial review, verification). What follows is the complete pipeline, with real numbers from my quant screener build: eleven merged pull requests, five archived change specs, and the bugs this process caught that I would not have.
The spec comes first
Every non-trivial change starts as an OpenSpec change: a proposal (why), a design doc (decisions, trade-offs, non-goals), requirement deltas with concrete scenarios, and a task list. One capability per change. This is spec-driven development with AI agents in the most literal sense: the spec is the artifact everything else derives from.
Writing the design doc feels like paperwork right up until it forces the actual decision — bar-count versus calendar-day windows for a moving average, what happens with fewer than 200 price bars, which side of an API boundary converts units. Agents amplify whatever you hand them. Hand them ambiguity and they’ll amplify that.
How do you get AI agents to review each other’s code — before it exists?
The step that has paid for itself the most: parallel reviewer agents attack the plan itself, before implementation. Three or four of them, each with a different lens (architecture, numerical correctness, testing, the domain), each prompted to refute, not summarize. The prompt skeleton, genericized from the one I actually run:
You are an adversarial <LENS> reviewer. Attack this plan.
Report only REAL defects, each as:
SEVERITY (critical / warning / nit)
— the defect, in one sentence
— a CONCRETE failure scenario: input/state → wrong outcome
If something is correct, don't praise it; move on.
Severity means something because it’s enforced: critical = a wrong result someone would trust, blocks implementation; warning = works but fragile, fix or explicitly accept; nit = polish. I read the reports and fold accepted findings back into the design doc — the plan that gets implemented has already survived its own red team (security jargon for “people whose job is to attack your work”).
Concrete example. Planning seven new screener fields, a numerical reviewer caught that my design computed a “50-day” moving average over a calendar-day window — which averages roughly 35 trading bars, not 50. Plausible code, correct-looking chart, wrong number, forever. Another reviewer challenged an assumption about data availability, so I probed the live source before committing the plan and adjusted it based on what came back. Total cost of that plan review: minutes of wall-clock and a few hundred thousand agent tokens. Finding the same bug in production would have cost my trust in every chart in the app.
Implementation happens in isolation, behind gates
The agent implements in a git worktree (a second working copy of the repository, so work-in-progress can’t touch the main checkout) against a snapshot of the database — never the live one. “Ready for review” is defined mechanically, not by vibes: linters, strict type checks on both stacks, the full test suite, and a check that regenerates all generated code and fails on any diff. No green gates, no review.
Two-pass AI code review: correctness, then red team
Once gates pass, reviewer agents read the actual diff — twice, with different postures.
The correctness pass asks: is the obvious thing wrong? Logic, tests, domain rules.
The red-team pass asks the more interesting question: what input did the author not
consider? A regression hunter, an edge-case fuzzer (nulls, NaN, zero denominators,
insufficient history), a numerical auditor checking formulas against references, and a
cross-cutting reviewer watching the multi-language contract. This multi-agent code
review earns its keep on inputs humans stop imagining: in one refactor, a reviewer
constructed a case where a date column arrived as a plain date object — not a
datetime, not a string — and slipped through a hasattr(col, "date") type check into
silent data loss. I would not have written that test.
On the screener-fields change, the same two-pass process returned zero criticals and zero warnings. I trust that result — cautiously — for three reasons: these are the same prompts that had caught real criticals on earlier changes in the same codebase; the reviewers still returned nits (a missing division guard, a test-coverage gap), which an agreeable reviewer wouldn’t bother with; and their reports contained verification evidence, not vibes — one had independently re-run both code generators and diffed the output, another had probed the live data source to check an assumption. A clean run from a process with a kill record is information. A clean run from a process that has never found anything is theatre.
Verify the behavior, not the diff
Tests passing is not the feature working. The last step drives the real thing: boot the app on real data, exercise the new behavior, capture screenshots. When I shipped mobile layouts, “verified” meant a Chrome-DevTools-Protocol probe measuring every element against a true 390-pixel viewport — which caught a layout overflow that eyeballing a resized browser window had missed twice. Measurement beats confidence; more on that below.
What doesn’t work
The honest section, because this workflow has scars of its own.
Reviewer agents default to agreeable. Everything above about refute-instructions, severity, and blocking exists because without them you get review theatre — five paragraphs of affirmation and a “consider adding a comment.” If your reviews never find anything, your reviews are the bug.
Agents are also confidently wrong in ways that burn hours. One insisted my headless screenshots proved mobile content was clipped; the actual bug was the screenshot tool misreporting the viewport width. Twenty minutes of arguing, zero progress — then one measurement probe settled it instantly. The lesson generalizes: when an agent and reality disagree, stop prompting and start measuring.
And some hours just die in dead ends. I spent a session trying to trigger GitHub Copilot’s PR reviewer through the API — GraphQL, REST, every plausible mutation — before proving it can only be requested through the web UI. The only thing that made that time worth anything is that it’s now written down in the workflow doc, so no future session (mine or an agent’s) repeats it.
Verdict
- Good: adversarial plan review — a silent numerical bug killed for roughly 350k agent tokens (what the four implementation reviewers cost on that change, for scale). The gate suite. Verification before “done”.
- Bad: the full cycle is heavyweight. For a README typo it’s absurd — I run it for changes where a wrong result would quietly compound. Calibration is a judgment call, and I sometimes get it wrong in both directions.
- Ugly: none of this replaces judgment. The workflow filters bad plans and bad code. Deciding what’s worth building — that stayed exactly as hard as it’s always been.
The workflow file, every spec, and every reviewed PR live in the quant screener project — built on an architecture with its own write-up.