GSD vs PAUL: I built the same app twice so you don't have to
Learn how GSD and PAUL compare in speed, token usage, and quality and which framework fits your project.
Two spec-driven frameworks for Claude Code and about 1.86 billion tokens. One framework was faster and the other was more careful, and which one you want depends on what happens after the demo.

- GSD lives up to its name: it gets shit done. In my run it was faster, asked me for less input, and left a readable, numbered history of the project. It’s my pick for proofs of concept.
- PAUL used ~35% fewer tokens (734.3M vs 1,125.5M), and its mandatory UNIFY step caught drift that GSD let through. It’s my pick for projects that will be maintained.
- At the scale of a single app, PAUL’s acceptance criteria and boundaries felt like ceremony. On cloud infrastructure work (Terraform, Terragrunt), that same ceremony is exactly what you want.
Why these frameworks exist at all
If you’ve used Claude Code (or any agentic coding tool) for more than an afternoon on a real project, you’ve seen this happen. The first hour is great. The model reads your code, picks up your conventions, and writes code that fits as if it read your mind. Three hours and a few hundred thousand tokens later, it forgets a decision you made at lunch, renames a function you told it not to touch, and says “Done!” about a feature that half works.
This is context rot. As the context window fills, the important early material (your goals, constraints and decisions) gets buried under tool output, diffs and the model’s own commentary. Output quality drops slowly enough that you don’t notice until something breaks.
The fix that most of the community has settled on is spec-driven development: write down what you’re building, break it into phases and plans and give the model a clean, curated context for each piece of work rather than one endless conversation.
GSD and PAUL both follow that philosophy. They disagree on two things:
- Where execution happens: in fresh subagents, or in the main session.
- What “done” means: the tasks are finished, or the result has been checked against a plan.
Most of the differences I saw stem from those two decisions.
This is a field report from one developer building one non-trivial app twice. It isn’t a benchmark suite. I’ll be explicit about what I measured, what I observed, and what’s just my opinion.
Meet the contenders
GSD: Get shit done
GSD is a meta-prompting, context-engineering and spec-driven development system originally created by TÂCHES. It became one of the most-starred Claude Code workflow projects on GitHub. After trust and ownership concerns about the original project, development continued under community governance as open-gsd, and that’s the version I used.
The core loop has five steps, repeated one phase at a time:
Discuss → Plan → Execute → Verify → Ship

The command surface is large. These are the ones you’ll actually live in:
| Command | What it does |
|---|---|
/gsd-new-project | Deep context gathering; writes PROJECT.md, REQUIREMENTS.md, ROADMAP.md |
/gsd-discuss-phase N | Adaptive questioning to pin down a phase before planning |
/gsd-plan-phase N | Research, plan, and verify the plan for a phase |
/gsd-execute-phase N | Execute plans with wave-based parallelization via subagents |
/gsd-verify-work N | User acceptance testing with auto-diagnosis |
/gsd-ship N | Create a PR from the completed phase |
/gsd-progress | Where am I, what’s next |
/gsd-quick | Ad-hoc task outside the roadmap |
/gsd-pause-work | Write a handoff before you stop |
Everything lives in a .planning/ directory:
.planning/├── PROJECT.md├── REQUIREMENTS.md├── ROADMAP.md├── STATE.md└── phases/├── 01-data-model/│ ├── 01-CONTEXT.md│ ├── 01-01-PLAN.md│ ├── 01-01-SUMMARY.md│ ├── 01-02-PLAN.md│ ├── 01-02-SUMMARY.md│ └── 01-VERIFICATION.md└── 02-.../
The key design decision is that the main session orchestrates and subagents do the heavy lifting. Each plan is handed to an executor subagent with a fresh context window. Independent plans run in parallel, grouped into waves. The orchestrator stays lean, so your main conversation never fills up with implementation detail.
If every plan runs in a clean context, context rot can’t build up. You pay for this by re-injecting context into every subagent, and you get speed and parallelism in return.
PAUL: Plan-Apply-Unify loop
PAUL, by Christopher Kahler, was written as an explicit reaction to GSD. Its repository includes a PAUL-VS-GSD.md that spells out the disagreement. Its tagline sums it up: “Quality over speed-for-speed’s-sake. In-session context over subagent sprawl.”
The loop has three steps, and the third is mandatory:
PLAN → APPLY → UNIFY

| Command | What it does |
|---|---|
/paul:init | Initialize the project with type-adapted requirements |
/paul:plan | Create an executable plan (auto-routes scope) |
/paul:apply | Execute the approved plan through an Execute/Qualify loop |
/paul:unify | Close the loop: reconcile plan vs actual, update state |
/paul:progress | Status plus a single recommended next action |
/paul:resume | Restore context after a break |
.paul/├── PROJECT.md├── ROADMAP.md├── STATE.md├── paul.toml├── ledger.toml└── phases/└── 01/├── 01-PLAN.md└── 01-SUMMARY.md
PAUL’s distinctive ideas:
- Acceptance criteria come first. Every plan defines AC-1, AC-2, … in Given/When/Then form before the tasks.
- Boundaries are hard constraints. A plan can declare what must not be changed, and violations are surfaced.
- Execute/Qualify loop. Each task is checked against the spec before moving on, and ends with an explicit status:
DONE,DONE_WITH_CONCERNS,NEEDS_CONTEXTorBLOCKED. - UNIFY is mandatory. Every plan ends with a
SUMMARY.mdthat records what actually happened compared to what was planned, which ACs passed, the decisions made, and the issues deferred. - Single-session execution by default. Subagents are reserved for bounded research and discovery, not implementation.
- One recommendation, not a menu. Where GSD tends to offer you options, PAUL recommends one path.
- Decimal phases. Urgent work becomes phase
8.1instead of forcing a roadmap renumber.
Execution without reconciliation causes drift, and drift compounds across sessions. Keep the full context in one session, check every plan against what was intended, and you’ll waste fewer tokens re-explaining the project to subagents.
The experiment
I built the same project twice, once with each framework. The project is a productivity tool, and on the surface it looks like many others. The screens aren’t the difficult part. Two things underneath them are.
1. A complex visibility model. Who can see an item depends on more than ownership. It depends on roles, on how and with whom the item was shared, and on rules inherited from whatever contains it. One feature can touch several of those rules, and one rule can affect most features. A mistake doesn’t show up as a crash. It shows up as a user seeing something they shouldn’t, or not seeing something they should. Both are silent and both are expensive.
2. Thorough unit tests. The only realistic way to trust a visibility model like this is to pin it down with tests: every role, every sharing path, and every inheritance edge case. So the test suite isn’t an afterthought in this project. It’s half of the specification.
Together, those two things make the project a good test for these frameworks. A to-do app wouldn’t separate them, because both would breeze through it. Here, a rule decided in phase 2 quietly constrains phase 6, and an agent that loses track of it writes code that compiles, looks right and leaks data. That’s exactly the situation where context management, drift control and verification stop being theory.

In a project like this, “the agent says it’s done” and “it’s actually done” can be far apart. A framework that checks results against explicit criteria and a framework that checks whether tasks were completed will behave differently when the test suite is the main source of truth. Keep that in mind when you get to Rounds 5 and 6.
Setup
- Tooling: Claude Code for both builds.
- Frameworks: open-gsd and PAUL.
- Same product spec, including the visibility rules and test expectations, for both builds.
People often ask whether the results depend on Pro vs Max. For this comparison they don’t. Plan type changes how fast you hit limits, not how many tokens a framework consumes for the same work. The ratios below hold either way.
- n = 1. One project, one developer, one build per framework.
- Order effects. Whichever build came second benefited from me already understanding the domain. I tried to keep my prompts and spec equivalent, but I can’t fully cancel this out.
- Model drift. The builds weren’t run at the exact same moment, so model behaviour may differ slightly between them.
Round 1: Getting started
Both frameworks begin by interrogating you about the project before writing any code. I’d count that as a strength of both.
GSD’snew-project → discuss-phase flow is thorough and conversational. It researches, asks follow-up questions, and turns your answers into requirements and a roadmap of numbered phases. Within each phase you get numbered plans. By the end of planning you have a clear structure of 01-01, 01-02, 02-01 and so on, and that structure becomes very valuable later (see Round 6).
PAUL’sinit → plan flow is tighter and more prescriptive. It pushes you toward acceptance criteria and boundaries from the very first plan. It also recommends one way forward instead of laying out options, which I found calmer. There are fewer forks in the road, and less of the “which of these four approaches do you prefer?” that leaves you second-guessing yourself.

Roughly even. GSD feels like a collaborative brainstorming partner, and PAUL feels like a senior engineer handing you a checklist. Which one you prefer probably says more about you than about the tools.
Round 2: Execution, subagents vs one session
This is the architectural core of the comparison.
GSD runs execute-phase, groups plans into waves, and gives each plan to a fresh executor subagent. Plans without dependencies run at the same time. The orchestrating session stays light and coordinates. You mostly watch commits land.
PAUL runs apply in the main session. It works through tasks one at a time, qualifies each one against the plan before moving on, and reports a status. Nothing has to be re-explained, because the session already knows the project.

Each model has a cost:
| GSD (subagent execution) | PAUL (in-session execution) | |
|---|---|---|
| Context freshness | Excellent, each plan starts clean | Has to be managed; relies on state files and plan closure |
| Parallelism | Yes, wave-based | No, sequential by design |
| Re-explaining the project | Every subagent needs context injected | Done once |
| Knowledge across tasks | Lives in files and summaries | Lives in the session and in files |
| Human interaction | Low, mostly hands-off | Higher, more checkpoints |
A subagent starts with nothing, so the orchestrator has to give it the plan, the relevant project context, conventions, and often the files it will touch. The subagent then usually re-reads more of the codebase to orient itself. Multiply that by every plan in every wave and you have a lot of tokens spent on re-learning rather than doing. PAUL’s whole token argument rests on this.
Round 3: Tokens
Here are the totals for building the same app:
| Tokens consumed | |
|---|---|
| GSD (open-gsd) | 1,125.5M |
| PAUL | 734.3M |
| Difference | 391.2M |
GSD used roughly 1.5x the tokens of PAUL. Put the other way, PAUL did the same job with about 35% fewer.

The architecture predicts this result. Most of the gap is the cost of fresh context: every executor, researcher, checker and verifier GSD launches has to be brought up to speed. PAUL brings the project up to speed once and keeps working in that context.
Whether 390M extra tokens matter depends on how you pay:
- On a subscription, tokens become rate-limit pressure. A 1.5x burn rate means you hit your window’s ceiling sooner, which costs you time.
- On API billing, tokens are money, and a 1.5x difference on a large project is a line item someone will ask about.
- In wall-clock time, the more expensive framework was actually faster (see Round 4). Parallel subagents burn tokens to buy speed.
The framing matters. GSD doesn’t waste tokens. It spends tokens to buy speed and hands-off execution. Whether that’s a good trade depends on what you’re building.
Round 4: Speed and how much input it needed from me
GSD finished faster, and it needed less input from me.
This is GSD’s biggest advantage, and the name is honest about it. Once a phase was planned, execution mostly took care of itself. Waves ran, commits landed, and I spent more time reviewing than steering.
PAUL asked more of me. The Execute/Qualify loop raises NEEDS_CONTEXT and DONE_WITH_CONCERNS exactly when it should, and every time it did, I had to stop and deal with it. This is PAUL working as designed, because it would rather ask than guess, but it means you can’t leave it running and go make coffee the way you can with GSD.
An independent spike reported in the GSD issue tracker found the same pattern. On a small multi-file maintenance task, GSD was 6.75x faster (4 minutes vs 27 minutes of active time) with zero blocking prompts versus one, while Seed+PAUL won on audit-trail quality. My project was much bigger and the ratio was smaller, but the direction was the same.
The fundamental disagreement
PAUL’s own docs put it like this: “AI is already the speed enhancement. We don’t need to optimize speed at the cost of quality.”
GSD’s implicit answer: sometimes speed is the quality you need. A PoC that exists on Friday beats a perfectly reconciled one that exists in three weeks.
Round 5: Drift, and the step that caught it
This is the round that changed my opinion.
UNIFY caught things. Every time PAUL closed a plan, it compared what was planned with what was actually built and wrote the differences down. Some of those differences were real: deviations that would have stayed silently in the codebase until a later phase built on top of them.
GSD isn’t blind to this. It has verify-work, verification documents, and an increasingly rich set of review commands. But its verification asks a different question:
- GSD asks:did the tasks get done, and does it work?
- PAUL’s UNIFY asks:did what we built match what we said we’d build, and if not, what changed and why?
The second question is the one that matters for long-lived projects, because drift is cumulative. Every unrecorded deviation becomes a false assumption for the next plan. In a data-heavy app like mine, a small mismatch in the model during phase 2 can produce a confusing bug in phase 7, and by then nobody remembers why things are the way they are.

The same spike in GSD’s issue tracker recommended adopting PAUL’s ideas: a /gsd-unify post-execution step that records “decisions made during execution”, BDD-style acceptance criteria, and a field that marks how much ceremony a plan needs. Unify was ranked the highest-impact of the three. When one framework’s users propose porting the other framework’s signature feature, that tells you something.
Round 6: Ceremony, acceptance criteria and boundaries
Here I have to be honest about PAUL. At the scale of this project, acceptance criteria and boundaries were mostly ceremony.
Writing Given/When/Then criteria for each plan, and listing what must not be touched, added overhead without catching much that UNIFY and normal review wouldn’t have caught anyway. On a single-repo application with one developer, the blast radius of any one plan is small and easy to see. You already know which files you’re touching.

That said, I don’t think this is a flaw in PAUL. It’s a question of scale, and in my experience these features become very valuable in cloud infrastructure work, especially with Terraform and Terragrunt.
In a Terraform/Terragrunt codebase, the blast radius of a “small” change is not obvious, and getting it wrong is expensive:
- Boundaries like “DO NOT CHANGE
live/prod/**”, “do not modify the networking module’s outputs”, or “no changes to remote state configuration” turn unwritten tribal rules into hard constraints the agent has to respect and report against. - Acceptance criteria map naturally to plan output: Given the staging stack, when
terragrunt run-all planruns, then there are 0 resources to destroy and only the expected resources change. - UNIFY gives you an audit trail of why infrastructure looks the way it does, which matters a lot when the next person (or agent) wonders why a security group rule exists.
In application code, a mistake usually means a bug you can fix. In infrastructure, a mistake can mean a recreated database, and that’s when ceremony starts to look like insurance.
So my view on PAUL’s ceremony depends on scale. It’s overhead on a small app, and it’s essential in a shared IaC monorepo.
Round 7: Reading the project back as history
This round surprised me, because on paper it should go to PAUL. Traceability is one of PAUL’s headline features.
In practice, my favourite thing about GSD was reading its progress as a history. Every phase and plan is numbered (01-01, 01-02, 02-01, …), each with a plan and a summary, in a predictable directory layout. Looking back through .planning/phases/ reads like a project log you can scan and understand. You can see what was built, in what order, with what intent, and what came out of it.

PAUL’s record is arguably richer (planned vs actual, AC pass/fail, decisions, deferred issues, explicit HANDOFF-{date}.md files). But GSD’s is easier to read through. GSD’s history reads like a story, and PAUL’s reads like an audit log, which suits auditors more than storytellers.
If you’re going to show the project history to stakeholders or onboard someone new, GSD’s numbered history reads beautifully. If you need to answer “why does this differ from the spec?” six months later, PAUL’s reconciled summaries hold the answer.
Scorecard
| Dimension | GSD | PAUL | Winner (for me) |
|---|---|---|---|
| Philosophy | Ship fast | Ship correctly | depends |
| Execution model | Parallel subagents in waves | Single session; subagents for research only | depends |
| Tokens (same app) | 1,125.5M | 734.3M | PAUL (~35% fewer) |
| Wall-clock speed | Faster | Slower | GSD |
| Human input required | Less | More (Execute/Qualify checkpoints) | GSD |
| Drift control | Verification-based | Mandatory UNIFY reconciliation | PAUL |
| AC and boundaries | Lighter | Given/When/Then plus hard boundaries | PAUL at IaC scale, ceremony at app scale |
| Readable project history | Numbered phase/plan log | Reconciled summaries plus handoffs | GSD (for readability) |
| Decision style | Offers options | Recommends one path | taste |
| Ecosystem and breadth | Large command surface, many runtimes | Smaller and more focused | GSD |

So which one should you use?
My verdict hasn’t changed since I finished the second build:
Use GSD when the goal is to prove something works:
- Proofs of concept, prototypes and hackathon builds
- Throwaway or short-lived projects
- When wall-clock time matters more than token spend
- When you want to plan, start execution and step away
It’s fast, it needs little input, and it leaves a project history that’s a pleasure to read.
Use PAUL when the project will be maintained:
- Long-lived products that will go through many sessions and many hands
- Codebases where drift compounds, such as complex data models and interlocking features
- Infrastructure as code (Terraform, Terragrunt), where boundaries and acceptance criteria stop being ceremony and start being guardrails
- When token budget or rate limits are a real constraint
It’s slower and asks more of you, but what it gives you back is code that matches what you meant to build.

The PoC-to-product trap
One pattern to watch for: PoCs have a habit of becoming products. If you start in GSD because it’s a PoC, and the PoC gets funded, you now have a fast-built codebase with a nice history but no reconciliation record. That isn’t a disaster, but it’s a good moment to stop and decide deliberately whether to keep going in GSD or move the next phase of work to PAUL.
You don’t have to choose one framework for everything. Even if you stay in GSD, you can borrow PAUL’s best habit: after every plan, write down what was planned, what actually happened, and why they differ. That one habit gets you most of UNIFY’s value, and it’s exactly what GSD’s own community has proposed adding.
A side note on autonomous agents
One more thought, and I can’t fully back this one up: fully autonomous agent setups, like OpenClaw-style agents that run with little human supervision, seem a more natural fit for GSD than for PAUL.
GSD already behaves as if nobody is watching every step. It needs little input, runs plans in parallel subagents, can work through the remaining phases on its own with /gsd-autonomous, and leaves clearly numbered files behind. PAUL goes the other way. Its Execute/Qualify loop is built to stop and ask a human, which helps when you’re at the keyboard and gets in the way when you’re not.
I haven’t tested this properly, and I’m not advocating for running autonomous agents against your codebase. Take it as a hypothesis worth testing, not advice. If you try it, I’d love to hear how it went.
Closing thoughts
Building the same app twice is tedious, and I don’t recommend it as a hobby. It did make one thing clear: these two frameworks aren’t competing to be the best at the same thing. They’re optimised for different moments in a project’s life.
GSD is the right tool for the first few weeks, when you’re finding out whether the idea works and speed matters most. PAUL is the right tool for the months and years after that, when the codebase has to keep matching what the team thinks it does.
The 391.2 million token difference is the visible number. The more important difference is what each framework treats as finished: for GSD it’s when the tasks are complete, and for PAUL it’s when the result has been checked against the plan. Pick the one that matches the kind of project you’re working on.
Appendix A: Command cheat sheet
Both projects move fast. Command names and directory layouts here reflect the versions I used. Check each project’s docs before copying them.
| Intent | GSD (open-gsd) | PAUL |
|---|---|---|
| Start a project | /gsd-new-project | /paul:init |
| Clarify a phase | /gsd-discuss-phase N | (part of /paul:plan) |
| Plan | /gsd-plan-phase N | /paul:plan |
| Execute | /gsd-execute-phase N | /paul:apply |
| Verify / reconcile | /gsd-verify-work N | /paul:unify |
| Ship | /gsd-ship N | n/a |
| Where am I? | /gsd-progress | /paul:progress |
| Stop / resume | /gsd-pause-work / /gsd-next | /paul:resume (+ HANDOFF-{date}.md) |
| Ad-hoc task | /gsd-quick | (decimal phase, e.g. 8.1) |
| Run unattended | /gsd-autonomous | n/a by design |
