Video · Claude Code

Claude Code’s Superpowers Plugin Is Mediocre Until You Do This

The numbers, the design doc, the code and the bugs behind a cloud workstation platform built in one evening.

2026-10-17Claude CodeSuperpowersGoAWSCloud workstations

On a small Go task, the Superpowers plugin for Claude Code got the same score as plain Claude Code and took 4.2 times as long. On a big one, it interviewed me, wrote a design and a 12-task plan, and built a cloud workstation service on AWS in one evening. The video tells the story. This page has what didn’t fit: the exact numbers, the prompt, what the design doc decided, the Go code that came out of it, and the 13 fixes its reviews made before anything touched AWS.

Quick context: I worked on the platform team for Netflix Workstations. Nothing internal was used here. I wrote the prompt from public talks only (linked at the bottom), and everything below is in the public repo.

The small task, in numbers

Superpowers shows up in nine of the twelve “best Claude Code plugins” roundups I looked at. To see if it actually helps, I first gave it the same job as my five-AI contest: build a Go URL shortener from a one-page spec, standard library only, checked by 55 hidden tests the model never sees. One run each, plain Claude Code against Claude Code with Superpowers installed.

Plain Claude CodeWith Superpowers
Hidden tests55 / 5555 / 55
Time1 min4.2 min
Model calls34
Input tokens120K183K (+53%)
Output tokens7.6K7.9K
Code lines · test coverage393 · 76.3%395 · 77.1%
Same spec, same hidden tests, same scorer as the five-AI contest. One run each.

Superpowers loaded its brainstorming skill, read the spec, and decided there was nothing to ask:

Claude Code loads the superpowers:brainstorming skill, reads SPEC.md, and says the spec is complete and unambiguous, so it is skipping the questions.
The skill’s first move is to ask questions. With a complete spec file, it had none.

That’s the right call, and it’s also the whole lesson. The plugin’s value is its process: questions, a design, a plan, then reviewed steps. When the spec already did that work, you pay for the process and get nothing back. The output was nearly identical: two more lines of code and less than a point more coverage.

The big build: a short prompt with lots of open choices

This is the whole prompt I gave it for the cloud workstation service:

Build a cloud workstation service for artists on AWS, in Go. An artist should be able to get a remote Linux workstation in seconds, connect to it from their own computer with Amazon DCV, move files onto and off it, and hand it back when they’re done. Admins should be able to define workstation types and scale pools of them up and down. Keep it cheap and safe for a demo: one region, the smallest instance types that work, every resource tagged, and nothing left running when we’re finished.

Same skill as before. This time it said there were a lot of open choices and asked before building anything, one question at a time:

Claude Code says this is a big project with a lot of open choices, then asks: tell me about the artists who will use this.
Its first question was about the people, not the tech.

The best question came next. It spotted a conflict in my own prompt: “in seconds” needs warm machines sitting ready, and warm machines cost money while they sit idle, which fights “keep it cheap.” It asked which promise mattered more. I picked a small warm pool, and that one answer shaped the whole design.

What the design doc decided

My answers became a design document. Two parts of it are worth stealing for your own projects.

It separates what I decided from what it decided. There’s a “Decided by the user” section and a “My calls (not yet confirmed by the user)” section. Its own calls included running the service on my laptop instead of hosting it, using a t3.medium for the CPU type, and a hard cap of 5 machines per pool. That made the review easy: I only had to read the second list closely.

It lists anti-goals: outcomes that count as failure even if everything technically works.

  • Anything left running or accruing cost after the demo.
  • Any AWS resource without the project tag.
  • Port 8443 open to anything other than my IP.
  • The service touching resources it did not create.
  • Creating anything in AWS before I’d seen the plan and said OK.
  • A sign-in screen that looks real but isn’t.

Every one of those shows up later as code. That list is the most useful thing the interview produced.

The plan, and the wait

Then it wrote a 12-task plan, and stopped:

Claude Code says the implementation plan is saved, it hasn't built, created or committed anything yet, and recommends subagent-driven development.
Nothing built, nothing created in AWS, nothing committed, until I said yes.

The order of the plan is the clever part. Tasks 1 to 11 build everything against in-memory fakes of EC2 and of the commands that run on each machine: the model, the pool logic, claims, idle and lifetime rules, the AWS code, the CloudFormation stack, the web pages, the CLI and teardown. Nothing in them creates anything in AWS. Task 12, the very last, is a written deployment plan and an approval gate before anything real exists. It recommended “subagent-driven” development: a fresh subagent implements each task, and another reviews it before the next one starts.

Where I had to correct it

The interview missed one thing, and I caught it while reading its summary:

Claude's summary says pool size is a field on each workstation type. My reply: I don't think pool size should be on the workstation type, it should be on the cluster type that contains the workstation, an n-to-1 relationship.
Pool size on the type would mean every pool of that type shares one size.

A type is the recipe: what kind of machine it is. A pool is the batch you’ve already made. One recipe can feed many pools, each with its own size. Here’s how the model looks after the correction:

// Type describes a kind of workstation.
type Type struct {
	Name, InstanceType, Image, Setup string
	Software                         []string
}

// Pool is a set of pre-warmed workstations of one Type.
type Pool struct {
	Name, Type            string
	Size, MaxSize         int
	IdleTimeout, MaxClaim time.Duration
}

Size and MaxSize live on the pool now, and Pool.Type is just a name pointing at a type. It felt obvious to me only because I’ve worked on this kind of platform, which is exactly the kind of knowledge an interview can’t ask about if it doesn’t know to.

How it works

A few design choices make this small (about 3,000 lines of Go) and hard to break:

  • No database. Each machine’s state (pool, claimed or warm, artist, timestamps) is stored as tags on its EC2 instance. Restart the service and it reads the tags back. The only thing lost is in-memory passwords.
  • No SSH. The service prepares machines with Systems Manager Run Command: restore the artist’s files from S3, create the Linux user, start the DCV session. No port 22, no key pairs.
  • One loop. Every 20 seconds it reconciles every pool to its target size and enforces the idle and lifetime rules.
  • Fakes for everything. All of AWS sits behind two small Go interfaces, with in-memory fakes. 165 test functions, and none of them call AWS.

This is the interface the whole service talks to:

type Cloud interface {
	ListMachines(ctx context.Context) ([]Machine, error) // project-tagged, not terminated
	Launch(ctx context.Context, spec LaunchSpec) (Machine, error)
	Save(ctx context.Context, m Machine) error      // persists m's fields as tags
	Terminate(ctx context.Context, id string) error // refuses non-project machines
	InstanceTypeExists(ctx context.Context, t string) (bool, error)
	GInstanceQuota(ctx context.Context) (int, error)
}

And this is the heart of the reconcile loop. Note the comment: it’s one of the fixes the reviews made.

unclaimed := len(launching) + len(warm)
switch {
case unclaimed < p.Size:
	// Claimed machines count towards MaxSize, the pool's cost cap, so a
	// refill never takes the pool past it.
	n := min(p.Size-unclaimed, p.MaxSize-len(machines))
	if n > 0 {
		errs = append(errs, s.launch(ctx, p, typ, n)...)
	}
case unclaimed > p.Size:
	// Too many warm machines: terminate the newest first.
	...
}

The pool size is a target, not a count. Hand a machine back and the loop launches a replacement to keep one ready. The loop never terminates a claimed machine.

What the reviews caught before AWS

The repo has 29 commits: 12 features, 13 fixes, and the rest docs and housekeeping. The fixes came from the review step after each task and a final review of the whole branch, all before the first deploy. A few that I wouldn’t have thought to ask for:

  • The cost cap didn’t count claimed machines. A pool capped at 5 machines, with 3 claimed, could still launch 5 warm ones: 8 in total. Fixed in the code above.
  • DNS rebinding. The service only listens on 127.0.0.1, but a hostile web page can point its own domain at 127.0.0.1 and talk to it from your browser. The fix rejects any request with an unexpected Host header, and Go’s cross-origin protection blocks forged form posts.
  • Teardown order. Teardown now waits for shutting-down machines before deleting the security group, and checks the project tag before every destructive step.
  • Scripts on stdin. SSM scripts used to arrive on bash’s stdin, where any command that reads stdin could swallow the rest of the script. They now run from a temp file, with a timeout that matches the client’s.
  • Retry storms. A machine that never boots now backs off its pool’s refill (30 seconds, doubling to 5 minutes) instead of relaunching right away.

Here’s the DNS rebinding guard, small enough to copy into any local Go tool:

func HostGuard(addr string, h http.Handler) (http.Handler, error) {
	host, port, err := net.SplitHostPort(addr)
	if err != nil {
		return nil, fmt.Errorf("listen address %q: %w", addr, err)
	}
	allowed := map[string]bool{}
	for _, n := range []string{"127.0.0.1", "localhost", "::1", host} {
		if n != "" {
			allowed[strings.ToLower(net.JoinHostPort(n, port))] = true
		}
	}
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if !allowed[strings.ToLower(r.Host)] {
			http.Error(w, "unexpected Host header", http.StatusMisdirectedRequest)
			return
		}
		h.ServeHTTP(w, r)
	}), nil
}

The root account mistake

None of those reviews could catch the worst mistake, because it was mine. When it was time to deploy, Claude asked me to log in to AWS, and I logged in as root: the master login that can delete everything and run up any bill. I hit Ctrl-C, came back with “sorry, I had to stop you in the middle of deploying because you were deploying as root,” and created a limited IAM user.

The check I run now, before any agent deploys anything:

aws sts get-caller-identity
# The Arn must end in :user/<name> (or an assumed role).
# If it ends in :root, stop.

The code has its own guardrail too. Every resource is tagged Project=workstations-demo, and Terminate refuses any machine without that tag, so even a bug can’t make it delete something it didn’t create.

The demo, and a one-line fix

Claiming is a name and a button. There’s no real login, and the page says so, because a sign-in screen that looks real but isn’t was one of the anti-goals.

A plain web page titled Claim a workstation, with a pool dropdown set to demo, a demo identity field labeled no authentication, and a Claim button.
The claim page. It shows the address, username and password for the DCV client once a machine is ready.

On the desktop my project files were there, copied in from S3, but there was no way to upload a file. The DCV session had been started without file storage. Claude’s fix was one flag:

A diff of internal/session/scripts.go adding --storage-root with the artist's home folder to the dcv create-session command.
--storage-root points DCV’s file panel at the artist’s home folder, which is also what gets saved to S3 on hand-back.

That fits the design: the home folder is what’s synced to S3 when you hand a machine back, so files you upload through DCV come back the next time you claim with the same name.

The bug it didn’t fix

Then I claimed another machine, and Claude noticed its own bug: the pool was launching machines and terminating them seconds later. After teardown, the EC2 console showed seven terminated machines for a pool that should only ever keep one ready.

The EC2 console in us-west-2 listing seven t3.medium instances, all terminated.
Seven machines for a pool of one. Teardown worked; the churn before it is a known bug.

It’s still open. If you run the repo, watch the instance count. The fix I’d add first is a clear “not ready yet” state on the claim page, so nobody can claim a machine that isn’t ready.

What it cost

Small taskBig build
Result55 / 55, same as plain ClaudeA working platform
Time4.2 min vs 1 minOne evening, plus a morning to deploy
Model calls4 (plain Claude: 3)207
Tokens183K in (+53%)25.2M in · 162K out
Token counts are from the Claude Code session transcripts. Most big-build input is the same conversation re-read on every call, which is served from cache.

25 million tokens sounds terrifying. But almost all of it is input, and most of that is cached rereads of the same conversation, which are cheap. It was all covered by my Claude subscription.

When to turn Superpowers on

  • Skip it when you already have a spec. If you can write down exactly what “done” means, the plugin will agree with you, slowly.
  • Turn it on when the project is big enough that you don’t know the whole plan yet. The interview pulls the plan out of your head, and the design doc gives you one short list to review instead of thousands of lines of code.
  • Read the “my calls” section and the anti-goals, not every line. That’s where your experience matters, like knowing pool size belongs on the pool.
  • Check who the agent is before it deploys. No plugin will do that for you.

Try it yourself

The code is cloud-workstations-go. It’s a demo, not production: no real login, no teams, basic logging, the churn bug above, and it creates billable AWS resources. Set a budget alert first. The README walks you through a non-root user, the costs and the GPU quota, and ends with one command to tear it all down, plus how to check that nothing is left:

bin/workstations teardown

aws ec2 describe-instances --region us-west-2 \
  --filters Name=tag:Project,Values=workstations-demo \
            Name=instance-state-name,Values=pending,running,stopping,stopped \
  --query 'Reservations[].Instances[].InstanceId' --output text
# should print nothing

The basic version was built in one evening. A decade ago, that would have taken a team months. And it still took a human to catch the pool design, the missing file storage and an agent about to deploy as root.

Want to see how Claude did against four other AIs on the same small Go project? That’s 5 AIs built the same Go app.

Project Details

Technologies

GoClaude CodeSuperpowers pluginAWS EC2Amazon DCVAWS Systems ManagerAmazon S3AWS CloudFormation