05.11, WebinarWiz: AI security and visibility in the cloudRegister now (PL)
18 min read

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 as a conveyor of numbered plans and PAUL as a closed Plan-Apply-Unify loop
Tl;dr:
  • 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:

  1. Where execution happens: in fresh subagents, or in the main session.
  2. 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.

What this article is and isn't:

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 GSD loop: discuss, plan, execute in waves, verify, ship

The command surface is large. These are the ones you’ll actually live in:

CommandWhat it does
/gsd-new-projectDeep context gathering; writes PROJECT.md, REQUIREMENTS.md, ROADMAP.md
/gsd-discuss-phase NAdaptive questioning to pin down a phase before planning
/gsd-plan-phase NResearch, plan, and verify the plan for a phase
/gsd-execute-phase NExecute plans with wave-based parallelization via subagents
/gsd-verify-work NUser acceptance testing with auto-diagnosis
/gsd-ship NCreate a PR from the completed phase
/gsd-progressWhere am I, what’s next
/gsd-quickAd-hoc task outside the roadmap
/gsd-pause-workWrite 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.

Gsd's core bet:

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

The PAUL loop: plan, apply, unify
CommandWhat it does
/paul:initInitialize the project with type-adapted requirements
/paul:planCreate an executable plan (auto-routes scope)
/paul:applyExecute the approved plan through an Execute/Qualify loop
/paul:unifyClose the loop: reconcile plan vs actual, update state
/paul:progressStatus plus a single recommended next action
/paul:resumeRestore 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_CONTEXT or BLOCKED.
  • UNIFY is mandatory. Every plan ends with a SUMMARY.md that 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.1 instead of forcing a roadmap renumber.
Paul's core bet:

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.

Visibility matrix of roles versus access paths
Why tests matter for this comparison:

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.
On subscription plans:

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.

Caveats:
  • 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.

A GSD plan and a PAUL plan for the same feature, side by side
Which onboarding felt better?:

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.

Execution timelines: GSD parallel subagent waves vs PAUL plan-by-plan in one session

Each model has a cost:

GSD (subagent execution)PAUL (in-session execution)
Context freshnessExcellent, each plan starts cleanHas to be managed; relies on state files and plan closure
ParallelismYes, wave-basedNo, sequential by design
Re-explaining the projectEvery subagent needs context injectedDone once
Knowledge across tasksLives in files and summariesLives in the session and in files
Human interactionLow, mostly hands-offHigher, more checkpoints
Why "fresh context" isn't free:

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
PAUL734.3M
Difference391.2M

GSD used roughly 1.5x the tokens of PAUL. Put the other way, PAUL did the same job with about 35% fewer.

Total tokens: GSD 1,125.5M vs PAUL 734.3M

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.

Tokens are not the same as money, or speed:

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.

Drift compounding without reconciliation vs resetting with UNIFY
The gsd community seems to agree:

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.

PAUL acceptance criteria and boundaries for an infrastructure change

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.

Where boundaries and acs pay off: iac:

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 plan runs, 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.

GSD's numbered phases read as a project history

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.

Both are valid:

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

DimensionGSDPAULWinner (for me)
PhilosophyShip fastShip correctlydepends
Execution modelParallel subagents in wavesSingle session; subagents for research onlydepends
Tokens (same app)1,125.5M734.3MPAUL (~35% fewer)
Wall-clock speedFasterSlowerGSD
Human input requiredLessMore (Execute/Qualify checkpoints)GSD
Drift controlVerification-basedMandatory UNIFY reconciliationPAUL
AC and boundariesLighterGiven/When/Then plus hard boundariesPAUL at IaC scale, ceremony at app scale
Readable project historyNumbered phase/plan logReconciled summaries plus handoffsGSD (for readability)
Decision styleOffers optionsRecommends one pathtaste
Ecosystem and breadthLarge command surface, many runtimesSmaller and more focusedGSD
Scorecard radar chart: GSD vs PAUL

So which one should you use?

My verdict hasn’t changed since I finished the second build:

Gsd is for getting shit done:

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.

Paul is for living with what you built:

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.

Decision flowchart: when to choose GSD or PAUL

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.

A pragmatic hybrid:

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.

Speculation, not a recommendation:

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

Version note:

Both projects move fast. Command names and directory layouts here reflect the versions I used. Check each project’s docs before copying them.

IntentGSD (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 Nn/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-autonomousn/a by design

Let's talk about your project

We'd love to answer your questions and help you thrive in the cloud.