Inside superlite: the philosophy, the gates, and the decisions that shaped it

How superlite actually works: the coordinator, scripts, hooks, and agent tiers; the plan → lint → dispatch → checkpoint → review loop; and the gates that enforce it.

Share

A deep dive into how an autonomous build workflow actually works: components, control flow, and the gates that enforce it.


In the introduction to superlite I covered what it is and showed the benchmarks. This post is about the reasoning behind it, and — more usefully — how the system is put together: who does what, what files move, which rules are code rather than prompts, and which decisions I reversed after measuring them.

Superlite's design document has about 45 numbered decisions. Many of them supersede earlier ones. That's not an accident. It's the main idea.

The rule that governs the design

The design document opens with one rule for itself:

The core design contains only mechanisms that were measured, smoke-tested, documented, or decided by the user. Everything else is an experiment, with the measurement that would promote it.

Every idea goes into an experiments table with a promotion gate. "Prime-then-assign cache sharing" became default when siblings 2–5 read all 24,876 prime tokens on their first call and wrote zero. "A 1-hour subagent cache" became default when an implementer resumed after seven minutes and read 26,857 cached tokens while the control re-wrote 11,699. The later slim prime (rules + plan header + stable interfaces, no plan body) became default when full-plan primes were writing ~26 KB into every dispatch and Sonnet cost was the gap versus superpowers.

If something can't pass its gate, it stays an experiment or gets dropped. That rule protected me from my own good ideas more than once.

Three equations

Everything in superlite follows from three claims about where time, money and quality come from:

  • Wall-clock time is set by the critical path. Not by how hard the model thinks. On the first benchmark, both workflows used about the same total model time. One just ran it in a line.
  • Cost is set by context re-sent per request. Every turn re-sends the conversation. A coordinator with a 130k context taking 80 turns is expensive whatever it's doing. So is shipping a 26 KB shared prime to twenty agents.
  • Quality is set by an objective oracle plus top-tier review. Tests the agents can't see, plus a reviewer strong enough to find what the tests don't cover.

Most of the design is these three claims applied over and over.

How a run actually works

Philosophy is cheap. Here is the machine.

The pieces

Layer What it is Job
Coordinator The main session (Opus, medium) Sizes the request, writes the plan, dispatches, records rulings. Never writes project code. Never runs raw tests.
Skills Injected prompts (using-superlite, brainstorming, writing-plans, SDD, …) Tell the coordinator when to call which tools.
Agents Seven named subagents in agents/ Models that design, build, review, or fix. None of them can spawn agents.
Scripts (sl …) Python/bash tools under subagent-driven-development/scripts/ Anything with a definite answer: lint, route, dispatch, checkpoint, tests, packet, finish.
Hooks Pre/Post tool hooks Enforce rules the prompt alone couldn't hold. Fail open on bad input.
Run state .superlite/ in the project stable.json, primes, pool, metrics, incidents, fix notes, test summary, review packet.

The agents today:

Agent Model, effort Job
implementer Sonnet, medium Builds one task, then stays idle for reuse
implementer-haiku Haiku, high Wave 0 skeleton, and Kind: mechanical tasks off the critical path; stops rather than guesses
package-reviewer Sonnet, medium Reviews a task or package and fixes its own findings
final-reviewer Opus, high Fresh one-turn agent; reads only the review packet; findings with a fix tier; never edits
fixer-sonnet Sonnet, medium Tier 1: cause known, one module
fixer-sonnet-high Sonnet, high Tier 2: cause known, spans modules or a seam
fixer-opus Opus, high Tier 3: cause unknown, or a lower tier failed twice

Control flow

flowchart TB subgraph E["Entry"] E0["Session-start hook<br/>injects using-superlite"] --> E1["Size request + who orchestrates"] end subgraph P["Plan"] P1["brainstorming"] --> P2{"Human or AI?"} P2 -- "human" --> P3["Review plan approved<br/>by a real typed reply"] P2 -- "AI" --> P4["Agent plan: task graph"] P3 --> P4 P4 --> P5{"lint --stable"} P5 -- FIX --> P4 end subgraph B["Build"] S1["Wave 0: skeleton + stubs<br/>implementer-haiku"] S2["Slim prime per tier"] B1["dispatch: ready tasks → idle pool"] B2["implementer reads slim prime,<br/>then sl task Tn"] B3["checkpoint: hidden refs/superlite/*<br/>+ seam checks + probes"] B4["Background tests"] B5["Selective package review"] end subgraph F["Finish"] F1["Review packet"] F2["final-reviewer: findings only"] F3["Fixers by tier + fix notes"] F4["finish: rulings, history, report"] end E1 --> P1 P5 -- STABLE --> S1 --> S2 --> B1 B1 --> B2 --> B3 B3 --> B4 B3 --> B5 B3 --> B1 B4 --> F1 B5 --> F1 F1 --> F2 F2 -- findings --> F3 --> B3 F2 -- "approved or fixes green" --> F4

A few invariants that hold for the whole loop:

  1. Stable work starts before the plan is perfect. lint --stable freezes tasks whose interfaces are clean and prints DISPATCH NOW: …. FIX tasks get patched while agents already run.
  2. Consumers don't wait on producers. They build against stubs and the plan's e.g. contract examples. When both sides checkpoint, a script runs the seam check.
  3. A checkpoint is not a commit. By default each finished task is saved under refs/superlite/*. Real commits on your branch happen only with /superlite:commit or sl autocommit on.
  4. The coordinator decides; scripts compute; agents judge. Ready sets, tiers, seam mismatches, and budgets come from code. Models write code, review meaning, and rule on open choices.
  5. No agent spawns agents. Subagents get explicit tool lists without Agent. Fan-out is the coordinator's job.

Data the agents see

This changed after the early "whole plan as prime" design, and it matters for cost.

  • Every fresh agent of a tier first reads the same slim prime: implementer rules, plan header, Global constraints, and each stable task's title, Owns, dependencies, and Produces/Consumes lines. No plan body, diagrams, or spec — so nothing in it goes stale while the coordinator is still patching FIX tasks. On one reference plan that was about 7.8 KB against 26.4 KB for the full prime.
  • It then fetches its own task text live with sl task Tn, which also prints the Produces lines of what that task consumes. Agents learn neighbour names from the current plan, not from a snapshot baked into the prime.
  • Haiku gets a still smaller mini prime. build_prime --prime full keeps the old whole-plan prime for cases that need it.

That is the current form of prime-then-assign: shared bytes first (so siblings share a cache write), task text second (so they don't read a plan the coordinator already rewrote).

Modes, not one path

/superlite:start (or plain language) picks a flow from the request:

  • Regular / architectural — the full plan → parallel build → review loop above.
  • MVP — plan on a clickable HTML prototype the user assesses, then the orchestrated build.
  • Feature — research existing code, a feature plan you validate, a prototype in its own worktree to try, then integration with frozen interfaces and seam checks. Human mode has a second gate after you try the prototype.
  • Debug — root cause first (systematic-debugging), then fix-only agents by tier.
  • Small — inline change, always with a reviewer; no task graph.

Same gates and same agent roster; different entry and approval shape.

Philosophy 1: rules in code, not in prompts

This is the core belief, and I learned it the hard way.

Early versions said in the skill text: "the coordinator never edits code" and "never run the test suite in the foreground." The coordinator did both anyway. Not always, but often enough that the build would finish at about 13 minutes and then 10 to 18 more minutes went on the coordinator reviewing, fixing and running raw pytest itself.

So those rules became hooks and script exits:

Gate What it enforces How
The coordinator can't write code Every change goes to an implementer or fixer; allowed writes are plans, specs, docs, and .superlite/** block-coordinator-edits hook
Human mode: nothing builds until you approve No lint/dispatch/wave0/prime until a real typed reply is on file hook + sl orchestrator approve
Tests never block the coordinator A raw pytest, npm test, go test, … is rewritten into background sl run_tests rewrite-test-runs hook
No hidden waits on tests A runner inside until/while or with stdout to /dev/null is denied; wait with sl run_tests --wait same hook + hang probe
No unsafe kills No pkill or taskkill /IM, only PIDs the run started block-unsafe-kill hook
No agent spawns agents Subagents get explicit tool lists with no Agent tool agent definitions
Every run is measured Each agent's launch and stop is recorded capture hooks → metrics.jsonl

How the coordinator guard works is neat: a probe showed that hook inputs from subagents carry an agent_id, and the coordinator's don't. One script can tell them apart.

There's also a detail I like. The guard is only active while a run marker exists. Originally that marker was the shared prime file, but lint runs before that file exists, and in that gap the coordinator wrote pyproject.toml itself. So the marker became stable.json, which lint writes first (alongside prime.md / pool.json). The gap closed.

The kill guard has its own story. In one benchmark, the superpowers arm ran taskkill //F //IM python.exe and killed both harness runners mid-run, its own and superlite's. Now no run can kill by name.

Hooks cost latency, so they're written in bash builtins only where possible. A spawned process costs about 260 ms on my machine. Rewriting the hooks that way took the kill hook from 1.3 s per call to about 340 ms and the coordinator guard from 2.3 s to about 340 ms. And every hook fails open on bad input: a broken guard shouldn't stop a build.

Philosophy 2: scripts for anything with a definite answer

If a step has one right answer, a script does it. Running tests, checkpointing a task's owned files, checking scope, computing which tasks are ready, scoring idle agents, linting the plan, building the shared prime, checking contracts, collecting the finish summary. None of these need judgment, so none of them get an agent.

Agents only do what needs a model: design, write code, review meaning, fix, and rule on choices.

That's why superlite 0.3 removed two agents it used to have. The test-runner (Haiku) became run_tests. The per-wave reviewer became a script-driven seam check plus selective reviews. The coordinator's job shrank to deciding, dispatching and ruling.

A related lesson came from token formats. I'd assumed YAML views would save 20–30% of tokens over JSON. I measured it: YAML was smaller than pretty-printed JSON on three of four datasets, but larger than compact JSON on all four, and the absolute difference was a few hundred tokens. So no YAML. What actually saves tokens is a short, purpose-built line like DISPATCH NOW: T2 T4 instead of a 35-field record when the agent needs 8.

Philosophy 3: no agent waits

Superpowers runs one task at a time by design. Superlite starts from the opposite rule: ready work starts the moment its dependencies are checkpointed, and nothing queues behind a busy agent.

That shows up everywhere:

  • The task graph sets the width. There's no fixed wave size. Early versions capped waves at five. That was replaced by "the best split, whatever the agent count." One reference run peaked at 18 agents at once; the dispatch cap is tuned separately (today it prefers a tighter concurrent set so prime writes don't dominate cost).
  • Idle agents get reused, busy ones never do. Finished agents wait in an idle pool with their cache warm. A ready task goes to the most relevant idle agent of its own tier. If none is idle, a fresh one starts.
  • Consumers build against stubs. A task that uses another task's interface doesn't wait for it. It tests against a stub and the producer's e.g. examples. A seam check reruns it against the real producer once both sides checkpoint.
  • Plans don't block agents. I'll come back to this under the gates.
  • Every test run is in the background, with one wait right before finish. A hang probe reports SUSPECT HANG from flat output/CPU/children; by default it does not kill.

The gates

Superlite's gates each exist because of a specific failure. The table above is the enforcement layer. Below is why each kind of gate is shaped the way it is.

Gate 1: the human gate (once)

Superpowers' stops were its slowest part, so superlite has one approval gate, then no stops except for destructive, outward-facing, or security-sensitive actions.

Before that gate, superlite sizes the request:

  • Trivial (a typo, a rename): just do it.
  • Bounded (a change to a flow that already exists in the repo): a short design in chat, one yes.
  • Architectural (a new project or subsystem): one batch of up to four questions, one design-and-plan document, one yes.
  • Spike ("is it possible to..."): say what it'll try in two sentences, then try it.

Questions are always batched. Never one per message. Who orchestrates is asked in that same batch (or skipped when the request already settles it: "decide yourself", away, headless → AI).

Then there's the choice of who orchestrates.

  • Human orchestrator. You get a plain-language review plan with diagrams. Nothing is linted, dispatched, or sent to an agent until a real typed reply approves it — a hook records replies, and sl orchestrator approve refuses to proceed without one newer than the review file. After that, scope changes become rulings, not more questions. (Feature mode adds one planned second stop: trying the prototype.)
  • AI orchestrator. Never stops for plan approval. Every choice it would have asked about becomes a ruling:
Ruling: <decision> - <why> - <cost if wrong>

The "cost if wrong" part is the important bit. When you come back, you don't just see what it decided. You see which decisions are worth checking first.

Gate 2: the plan gate (lint --stable)

The plan is a task graph. Each task has a fixed shape:

  • Owns: exact file paths. Two tasks that can run at once never share a file.
  • Consumes / Produces: exact signatures. An implementer only reads its own task (via sl task Tn), so this is how it learns what neighbors named things.
  • Kind: design or mechanical.
  • Review Focus: the hostile cases this task's files must survive, as named tests.
  • Est. time and Est. context: predicted by a script from the task's size.
  • Test code: verbatim by default (detail: tests).

Lint enforces the rules a human would forget. SIZE: fails a task predicted over 6 minutes. OPUS-TASK: rejects Opus implementers. KIND: rejects a "mechanical" task that isn't really mechanical. CHAIN: fails a plan whose critical path is more than 2.5× the longest task, unless there's a ruling that names the exact chain id lint printed. A ruling written before lint ran can't match, so you can't pre-excuse a chain.

The interesting part is --stable. Lint splits the plan into STABLE tasks (their checks pass and every interface they consume is cleanly produced somewhere) and FIX tasks. Stable tasks dispatch immediately. Only then does the coordinator patch the FIX tasks and re-lint.

That came from a measurement. The coordinator writes the whole plan in one go, about 16.7 KB, and in one run the first agents didn't start until 322 seconds in. Replaying lint on that plan's first draft showed three tasks could have started at 104 seconds. The rule now is: no agent waits on plan polishing.

Once a task is dispatched, its block is frozen. If a later edit changes it, lint reports FROZEN:. A real interface change becomes a follow-up fix task, not a silent rewrite under a running agent.

Gate 3: the routing gate

The same plan always gets the same agents. A script reads each task's features and picks its tier from a rules table in routing.json. The coordinator never passes a model to an agent call, and a task only goes to an idle agent of its own tier.

Three decisions behind the agent table are worth explaining.

No Opus implementers. Opus writes about half as fast as Sonnet. On one benchmark, superpowers' two Opus implementers ran the longest tasks (6.6 and 9.0 minutes, one after the other) right on the critical path. In superlite, a task that seems to need Opus gets split. The part Sonnet can't get right goes to fixer-opus.

Haiku only for mechanical tasks off the critical path. "Mechanical" has a strict definition: 150 lines or fewer, at most two components, fast tests, exact test values, and no Review Focus case involving deep input, numbers, state or protocols. In an A/B on one such task, Haiku cost about a tenth of what Sonnet did and took about 12 seconds longer. That's a great trade off the critical path and a bad one on it. So a critical-path task never goes to Haiku, and a Haiku miss is never retried on Haiku. It goes straight to a Sonnet fixer and gets logged, so the first-time pass rate can be measured.

Haiku never fixes. There used to be a fixer-haiku tier for exact file:line changes. I removed it. The reason fits in one sentence: a fix keeps a decision. Even a "simple" fix decides what the right behavior is, and that's not mechanical work.

Gate 4: the review gates

Review has changed more than anything else in superlite, because it's where I was most wrong.

Superpowers reviews every task, with re-review loops. On the first benchmark that was about 12 of its 34 minutes. Superlite 0.1 replaced it with one review per wave, and the final review was a 15-second pass.

Then a benchmark separated them on quality for the first time. Both builds of the spreadsheet engine passed all 88 hidden tests. An independent Opus reviewer then probed them with hostile input. Superlite's build crashed with a RecursionError on a formula nested 76 levels deep. Superpowers' build returned the right answer, because its own Opus final review had found that crash and its fix loop had fixed it. That was most of its extra 30 minutes.

So the hidden tests measure spec conformance, not robustness. And a 15-second review doesn't probe hostile input. Three things changed:

  1. A robustness score was added to the benchmark: seven probes beyond the spec, reported separately.
  2. Review Focus went into every task in the plan: the specific hostile cases (deep recursion, huge input, non-finite numbers, invalid encodings, unsupported methods) its files must survive, as named tests.
  3. The final review became a fresh Opus agent at high effort, reading a review packet built from the run's logs.

Today review runs at three levels:

flowchart LR C[task checkpoints] --> S{seam, complexity ≥ 5,<br/>Review Focus risk,<br/>or open incident?} S -- yes --> R1[review this task now] S -- no --> R2[one review per package] C -.-> SC[seam check by script] R1 --> P[review packet] R2 --> P SC --> P P --> F[fresh Opus final reviewer<br/>findings only]
  • Seam checks are scripts. When the second side of an interface checkpoints, a background script checks the contract. A model only gets involved on a mismatch.
  • Package reviews are selective. A task gets its own review only if it's on a seam, has complexity of 5 or more, touches a Review Focus risk, or has an open incident. Everything else is reviewed once per package.
  • The final reviewer reports findings only. It reads only review-packet.md (open incidents, probe failures, contract mismatches, failing tests, and source excerpts around each hotspot) and answers in one turn. Each finding comes with a fix tier. It never fixes anything.

The source excerpts were their own lesson. In one run, the packet had no code in it, and the final reviewer gave up after 16 seconds. That kicked off an improvised extra review. Now the packet carries the changed hunks of the hotspot files, capped at 120 lines per file.

And when the packet is clean (no open incidents, no probe failures, no mismatches), there's a fast path: Verdict: APPROVED and stop.

Gate 5: the fix gates

Every fix goes to a fix-only agent, routed by how well the problem is understood. After two failed rounds at a tier, it moves up one.

The trick that makes escalation work is fix notes. Subagents can't compact their own context, so the working state of each fix lives in a file, .superlite/fixes/<id>.md, capped at 1,500 characters: goal, where, what changed, what was tried and failed, the evidence, and the best next hypothesis. Each round rewrites it.

A later round never resumes the old session. It starts a fresh agent whose only brief is "read the note first." That keeps each round at roughly 10–20k tokens of context instead of a reused 60–100k session. It's compaction, done in a file.

Gate 6: the budget

A plan can carry a target time and a cost cap. sl budget warns at 70% of the cap. Over the cap, it stops optional work: no extra review rounds, no fix rounds beyond the first. If the run is late, it names the critical-path task to unblock first. These are soft controls for now. A hook that refuses new agents over the cap is designed but not built.

Cache is architecture

Prompt cache is not a free lunch; it is a write-once, read-many bet. The first agent that sees a given prefix pays a cache write. Later siblings that start with the same bytes read that write instead of paying again. That showed up cleanly once: siblings 2–5 read all ~24,876 prime tokens on their first call and wrote zero. Break the shared prefix — different plan body, different effort, different model — and every sibling pays the write again.

That is why the slim prime must be byte-identical across a tier, and why a full-plan prime (26 KB) made Sonnet cost the gap versus superpowers: many dispatches, each writing a large prime. Slimming the prime to rules, header, constraints, and stable interfaces (8 KB) cuts the write. Putting each task's body in the prime would bust the share: every agent would start with a different prefix. So the shared write stays small and stable; each agent fetches its changing task text after the prime with sl task Tn.

Model and effort live in that same accounting. Each model has its own cache, and changing effort invalidates the blocks already written — so one effort level per agent for its whole life, with variation only across separate agents. The coordinator stays at medium; the final reviewer is a fresh Opus agent at high. And because fix rounds often arrive more than five minutes after an implementer's last call, a 1-hour implementer TTL (via CLAUDE_CODE_SUBAGENT_PROMPT_CACHE_TTL=1h) keeps the history warm: one resume read 26,857 cached tokens while the control re-wrote 11,699. Putting experimental: cacheTtl inside agent frontmatter turned out to be unsafe — Claude Code could ignore the model: line next to a nested block, and Haiku agents could silently run as Sonnet — so the TTL belongs in the environment, not nested in the agent file.

  • One effort level per agent, for its whole life. Vary effort only across separate agents.
  • Identical slim prime per tier, then live sl task Tn. Share the write; don't put per-agent plan body in the prefix.
  • A smaller prime for Haiku. The mini prime is about 1k tokens instead of several thousand. On the A/B task it saved about 9 seconds and a third of the cost.
  • A 1-hour implementer TTL in the environment. So later fix rounds read cache instead of rewriting it.

The benchmark lessons that cost a run each

Building the benchmark taught me as much as building the plugin:

  • A headless session repeats its cumulative cost every time the coordinator idles. My first harness added them all up, which inflated costs three to nine times and flipped my published cost conclusion. Score the last result per session.
  • A host session's environment can silently disable plugin hooks. Strip CLAUDE* and ANTHROPIC* variables before each run.
  • "Let it choose" doesn't produce orchestration. When a decision-maker played the human and took each workflow's recommendation, both built inline. The comparison was inline against inline. Every benchmark prompt now leans toward orchestration.
  • Overlapping runs share rate limits. Run reps one after another, or at least record the overlap.
  • A test suite at its ceiling can't measure review. Everything passed 177/177. The robustness probes are where the difference shows.
  • Per-agent overhead is real money. On the combined reference run, Opus cost was at parity with superpowers; the gap was Sonnet, mostly from many dispatches each writing a large prime. Slim primes are the first lever; bundling tasks per agent is still an experiment.