Reading time:
The documentation pipeline we layered onto Superpowers
This is part two. Part one covered why we layer onto Superpowers instead of forking it, and how we attach steps to lifecycle moments rather than to skill names that any release can rename.

Article written by
Ali Tavakoli
Meta: The gates, insertion points, and the one constraint that shaped a spec-and-traceability pipeline built on a methodology we don't own.
This is part two. Part one covered why we layer onto Superpowers instead of forking it, and how we attach steps to lifecycle moments rather than to skill names that any release can rename.
Quick recap of the setup: Superpowers is a plugin that installs process skills (brainstorming, planning, TDD, verification) and routes each request to whichever one fits. We never edit its files. Everything we add lives in files we own, and when it updates, we pull the update.
This part is the thing we added: a documentation pipeline that produces our specs, traces every test to an approved decision, and knows when to stay out of the way.
Gates: not every request deserves the documentation machinery
Our first version was bad here, and fixing it mattered a lot.
We had built a documentation pipeline: product spec, technical spec, ID registries, traceable tests. Excellent for a feature. Absurd for “add a date-formatting helper.”
And our first version ran it for everything. A request to add one utility function triggered our spec-writing skills, which correctly refused, because a utility isn’t a product feature and has no acceptance criteria. Then the agent treated that refusal as a gap to resolve and stopped to ask about it. Every time.
So you may say, isn’t that just a tuning problem? That is exactly what it looks like, which is what makes it dangerous: the workflow was correct and unusable, so we kept adjusting wording instead of fixing the design.
The fix made skipping a first-class declared decision rather than something the agent infers. Two yes/no questions, each answered on its own terms:
Is this work producing a single-purpose library of a kind that can be a product feature? A utility, a proxy, an application shell, or a bucket library holding unrelated items? No. Those never form a feature, so skip the whole documentation set.
Does this existing library’s feature already have specs? If not, do we document it now? On the build workflow we ask, because that’s already a design conversation. On the bug-fix workflow it’s automatic, because a bug fix is the wrong moment to start a first-time spec interview.
Then the machinery around them, which is where the value sits:
Declare the skippable steps once, as a named set: its members, how multiple answers combine, and, critically, what runs regardless. A gate answering no skips documentation. It never skips writing tests, following the plan, or verifying the work.
Answer each gate on its own terms, and never reference another gate’s answer. We tried cross-referencing them once (“when the other gate applies”) and it reads two opposite ways, both defensible. That’s a bug in a prose contract.
Skip cleanly and completely. Not “skip the specs but keep the ID conventions”. When the set skips, it skips whole, and the workflow falls back to vanilla Superpowers behaviour. Half-applied conventions are worse than none, because nobody knows which rules were in force.
Important! Add an explicit reassurance to the workflow file, because agents are conscientious and will otherwise go hunting. When a request has no library under test at all, e.g., a broken pipeline or a slow build script, both gates answer no, the whole set skips, and that is the correct outcome. Not a gap.
What we inserted, and where
With attachment points and gates in place, the additions are small.

Our insertion points (◆) and our two stopping points (■). Everything unmarked is Superpowers’ own. The AC approval stop gates the technical spec, not just the product one: a technical spec built on unapproved criteria turns one rejected criterion into orphaned requirements across two documents.
Before brainstorming: load the context that shapes the questions
This is the highest-return, lowest-effort change we made.
Superpowers' brainstorming skill is good. It asks clarifying questions. But which questions it asks depends on what it knows when it starts, and once it starts, it is too late to shape it. An agent that has read our library types, our naming conventions, and this feature's existing specs will ask something useful, like "does this need its own data layer, or does it consume an existing one?" An agent that hasn't read them asks something basic, like "what should we call it?"
So the reads that shape the conversation need to happen before the skill starts talking. Not in a workflow file, which loads too late. They go in AGENTS.md, as a short checklist the agent works through on its own, never by interviewing the user.
The checklist is biased on purpose: if unsure, answer yes. A wrong yes costs one extra file read. A wrong no skips a convention quietly, and nothing downstream catches the mistake. When the two failure modes are that lopsided, it isn't worth trying to be precise.
Before planning: write the specs, then decide about end-to-end tests
The plan gets drafted from the specs, with the brainstorm's output filling any gaps the specs don't cover. The order of precedence is explicit: where the two conflict, the specs win. That is where a human made the decisions, in a structured interview.
One note. In the version we run, brainstorming says the only skill you invoke after it is writing-plans. We insert document-writing skills in that gap. The precedence rule allows this: the exclusivity is there to stop implementation skills from jumping straight to code, and ours only write documents. Read the skill you actually have installed rather than trusting that sentence here. And if you do the same, be ready to explain why you are overriding a stated rule.
The end-to-end decision has to happen before planning, for a practical reason. We found nothing in Superpowers that treats end-to-end tests as their own kind of work, so nothing prompts you to consider them. And a task added after planning can't carry the file paths and test code that every task in a good plan needs. So decide first, or you won't decide well.
After planning: enrich the plan
This is the part that is hardest to anticipate needing, so it gets its own section next.
Before finishing: verify the docs against what shipped
Every entry in those specs carries a stable ID, and every test title cites one. That way a test traces back to an approved decision, rather than just describing whatever the code happens to do. A developer, a QA engineer, and a product manager can all point at the same ID, instead of describing one behaviour in three different vocabularies. That is what makes the specs worth maintaining across teams.
So keeping those IDs accurate is its own step. Every requirement the cycle touched ends up as one of four outcomes: added (create a new ID), amended (fix the text under its existing ID), retired (move the entry to a burned list and never reuse its number), or unchanged.
What matters here is where this step sits. Implementation is where the code starts to drift from the spec, so this is the only point where the two can be brought back together. And this step always runs. Only its actions are conditional. We tried running it "only when new IDs were minted," and it missed every amended requirement, every retirement, and every stale index row. There is no "did anything change?" question to get wrong if you just always check.
What keeps it cheap is scoping the walk to what changed. Open the technical spec's ID index first, since it maps every ID to its file and its acceptance criterion, so it tells you what else to open. Cross-reference that with the libraries this branch touched, and walk only those in full. Everything else is unchanged by definition. On a one-line fix, that is the difference between a quick check and a full re-read.
One carve-out. If a shared library's behaviour changed, that reaches consumers outside this branch, so flag those rather than assuming they are fine.
The plan is the only carrier
In subagent-driven-development, Superpowers' automatic execution mode, implementation happens in isolated implementers: one agent per task, each with a fresh context. They don't read AGENTS.md. They don't read our documentation. They can't load a skill. All they read is the plan.
So anything an implementer must obey has to sit inside the plan before execution starts. Not implied by the workspace. Not stated in a skill. Written into the plan. This is a useful constraint, because it turns an abstract question into a concrete one. "Does the agent know our test conventions?" becomes "is it in the plan?", which is something you can actually check.
This is the clearest example of a broader rule: prefer a durable path over held context. A file that gets read when it is needed does not care how long the session has run.
Anything merely held in context, though, degrades as the window fills up. So load what you need to reason with, and pass a resolvable path for anything someone else will need to follow. This splits our helper skills cleanly. Whoever plans loads their rules. Their worked examples get passed on as paths to whoever builds. A later step re-reads instead of assuming an earlier step's context survived, because you can't tell by looking whether a memory has gone stale, and re-reading only costs one file read.
So we have one skill whose whole job is to fold our conventions into the plan after writing-plans produces it. Here is what it adds:

Three of its properties are worth copying whatever tooling you use:
Carry pointers, not copies. The plan references the specs. It isn’t a second copy of them. A copy goes stale between writing and reading, and by then nobody remembers it was a copy.
Make every pointer a resolvable repo-relative path. This follows from the isolation. An implementer can open a file and can never invoke a skill, so “build it to the canonical example” is not a pointer. It’s a phrase the agent has no way to resolve.
Keep each injected rule to one line. That block gets re-sent in every task dispatch, so its length multiplies by the task count. A wordy constraint block is a tax you pay per task, competing with the context the implementer needs for the actual code.
And notice the last constraint in that example. It exists purely because the isolation is real. An implementer who discovers that a requirement is wrong must report it by its exact ID, with what differs.
Never edit the docs, never invent a replacement ID, and never quietly code around a requirement it believes is mistaken. Docs get reconciled after execution, and that report is the only signal that reaches the reconciliation step. An unreported divergence ships as a document contradicting the code, and nothing will catch it.
Two mechanisms worth more than they look
A hard stop between planning and building. After enrichment, the workflow stops. It does not create the branch, and it does not start coding. It reports that the plan is ready at its path and then waits.
I resisted this at first, because it adds friction to a system built on momentum. But it turned out to be the most useful checkpoint we have. Reviewing a plan is quick. It catches misunderstandings that would otherwise show up several commits later, when they are far harder to fix. At the plan stage, changing your mind is cheap.
There is a second benefit that took me longer to notice. The stop is also a clean handover point. By the time a plan is ready, that session has read the library conventions, run a brainstorm, produced a product spec and a technical spec, and enriched a plan. Almost none of that work is needed to actually execute the plan. So we now recommend handing the plan path to a fresh agent. It starts with an empty window and reads only what the plan carries.
This works because of the durable-path rule. If the plan carries everything it needs, handover is easy, and execution runs without competing for space against the leftovers of a spec-writing session. The dated line described below is what makes this safe to do days later. You can also just continue in the same session. The only cost there is less room to work in.
Date the plan. Superpowers writes its spec and plan wherever your preferences say, and ours go to a path that git ignores.
This might sound like throwing away the record, so it helps to be clear about what the record is. Our specs are the real decision artifact. They are committed, reviewed, and carry the IDs the tests reference. We maintain them for as long as the feature exists, not just for one cycle. They describe how a feature works at both the product and library level, so they answer far more questions than a plan could. The plan is a smaller thing. It captures how we sequenced this particular piece of work, and it is useful for days rather than years. Leaving it out of git loses nothing, because the specs were always the better record.
Keeping the plan out of git has one side effect: nothing else can date it. So the plan dates itself. It carries a line in its Global Constraints that records the day it was declared ready. This matters because a plan is only safe to execute against the documentation and code it was written from.
So when someone hands over a plan path, maybe days later, maybe in a different session, check one thing. Has anything under this feature's docs, or the libraries these tasks touch, been committed since that date? If so, re-read and re-confirm before executing. Running a stale plan as if it were current is an expensive mistake, and checking one date prevents it.
One level deep, and no further
When you brainstorm a feature, you often find that another library needs work too. Maybe a shared component needs a new input, or you need to create a utility. We call this companion work: a library outside this cycle's feature that you have to create or update so the feature can work.
Plan the companion work as part of the cycle. Order it before whatever depends on it. Point it at its own source of truth, and note clearly that none of our requirement IDs apply to it. Requirements stay with the library that owns the behaviour, not the library that needed it.
The rule is to check one level deeper, and stop there. If a companion needs work in yet another library, that work does not belong to this cycle. Instead, report the chain: name the libraries involved and which of our acceptance criteria are at risk. Then let the user decide what to do. They can widen the cycle, do the deeper work first in its own cycle, or defer it.
Without this limit, one requested feature can expand into an endless series of documentation cycles, each needing its own approval. An agent will start down that path on its own, because it looks like progress even though it pulls in far more work than the request called for.
This rule is a scope policy, not a way to resolve dependencies. Your build tool already maps the code graph, and it does that better than any convention could. What the build tool cannot decide is how much work and documentation a single request should pull in. That is a judgement about scope, so a person should make it. The depth cap exists so that question actually reaches them.
One more thing: if the user defers a chain, record that decision in the plan. Write down the chain, note that it is deliberately out of scope, and list which acceptance criteria are at risk. The plan is the only thing that carries this forward. A decision made only in conversation won't reach the next session that picks up the plan.
What it costs
You live downstream of someone else’s design, permanently. Layering means accepting Superpowers’ shape. When it lacks a concept you need, e.g., end-to-end tests in our case, you work around the gap instead of closing it, and every workaround is yours to maintain.
Upgrades need attention. The whole point is that you can pull updates, but each one needs checking: do the skills we anchor to still exist, and do their descriptions still say what we route on?
Duplication into the plan is forced. Conventions live in two places: in your documentation, and injected into every plan. That’s a known drift site, and it needs sweeping whenever a convention changes.
The hard stop costs momentum by design. Someone has to read the plan. On a team that will not read it, you lose the review half of the value, though the fresh-session handover still stands on its own.
So why keep paying? Because a workflow is a thing you keep. Superpowers will change, your conventions will change, and your codebase will change, all on different schedules. The costs above are what let those three move independently instead of dragging each other. The alternative is a fork that stops receiving improvements, or a set of conventions that stops being applied because nobody noticed which insertion point died.
Where to start
Don’t customise on day one 🙂
Adopt the methodology unmodified for a few real cycles. You can’t see where your conventions need to attach until you watch the default flow miss them.
Add the pre-flight reads first. Loading the right context before the first question gets asked is the cheapest change with the most visible effect on output quality, and it needs no gates, no skills, and no plan enrichment.
Add gates before you add steps. A documentation step with no skip condition fires on a one-line utility change and makes the whole workflow feel like a tax. Build the skip path before the feature path.
Then add one insertion point at a time. After each, run a cycle that should not trigger it. That’s how you test the gate.
Enrich the plan only when you actually use isolated execution. If your agent runs in-session and can read your instruction file, you don’t need the carrier yet.
Record the revisit condition for every decision as you make it. One sentence, and it’s the only thing that makes the next upgrade tractable.
A note on shelf life
Everything above is what works for us today, and this space is moving quickly. If you are reading this a year or two from now, some of it has aged. Not all of it at the same rate, though:
Goes stale fastest: every skill name in this article. Superpowers is young and improving, so brainstorming, writing-plans and the rest may be renamed, split or replaced. Our own misanchored hook is proof that happens. Check what is actually installed before you attach anything to it.
Ages slowly: layering instead of forking, anchoring on lifecycle moments rather than names, declaring skip conditions, and carrying rules to whoever executes them. Those apply to any tool you adopt and then need to bend.
If what you see in the current tools differs from what I describe here, trust what you see. Take the reasoning, and rebuild the specifics against whatever is true when you read this.
Want to learn more?
obra/superpowers is the methodology we build on, including the precedence rule that makes layering possible.
open-gsd/gsd-core and bmad-code-org/BMAD-METHOD are two alternative takes, both worth understanding before you commit to one.
anthropics/skills and mattpocock/skills are collections of individual skills, useful for calibrating how a good skill gets written independent of any methodology.
AGENTS.md is the open convention for the instruction file every customisation above ultimately lives in.
Ali Tavakoli

Article written by
Ali Tavakoli



