Five questions your coding agent has to answer in plain English, before it changes a single file. Then you type a phrase, and only then does it start writing.
The free, open edition of OUTER. A maintained commercial edition, Super-outer, continues the line. Details below.
npx skills add Shivak11/outer-engineering-loopEvery one of them exists because of a specific way an AI coding agent ruins your afternoon.
| The question | What it stops | |
|---|---|---|
| O | Outcome. What should be true when this works? | The agent solving a different problem, confidently and fast |
| U | Unacceptable. What must never happen, even once? | Charging the card twice. Logging everyone out. The thing nobody thought to forbid |
| T | Trade-offs. What are we giving up? | A decision made silently on your behalf, found three weeks later |
| E | Evidence. What would prove this works, beyond "looks right"? | "Done!" on code nobody ran |
| R | Recovery. How do we get back if it is wrong? | Discovering there is no way back, at the worst possible moment |
You answer these in ordinary language. The agent translates them into mechanisms. It never asks you to pick a database, a queue, a caching strategy, or a library, because those are its job and not yours.
Here is one decision, asked twice.
How a coding agent usually asks: "Do you want optimistic locking on the seat row, or a pessimistic lock with a transaction?"
How OUTER asks: "If two people claim the last seat at almost the same moment, can one of them be told to try again, or must both get a final answer immediately?" [race condition]
Same fork in the road. Same consequences. Only one of them is a question you can actually answer, and you are the only person in the room who knows the answer.
That is the whole design. You rule on the product. It handles the engineering word in brackets.
An AI agent hits the same forks in the road that a senior engineer does. The difference is who it asks, and how.
Most agents do one of two things. They decide silently and tell you afterwards, or they ask you the engineering question directly, which you cannot answer, so you say "whatever you think is best" and the decision leaves your hands anyway.
OUTER asks the same fork as a product question. Same decision, same consequences, asked in a language you actually rule on.
| What a coding agent usually asks | What OUTER asks instead |
|---|---|
| "Should I make this endpoint idempotent with a dedupe key, or add a unique constraint?" | "If someone taps Pay twice because the screen froze, should the second tap be quietly ignored, or should they see an error?" |
| "Optimistic locking or a pessimistic row lock?" | "If two people claim the last seat at almost the same moment, can one be told to try again, or must both get a final answer immediately?" |
| "Should I invalidate the cache on write or use a TTL?" | "After someone edits their profile, is it acceptable for others to see the old version for a minute, or must the change be visible everywhere at once?" |
| "Soft delete or hard delete?" | "When someone deletes a recording, should we be able to get it back if they ask tomorrow?" |
| "Do you want retries with exponential backoff?" | "If the upload fails on a bad train connection, should it keep trying quietly in the background, or tell them straight away and stop?" |
Notice what changed. Not the difficulty of the decision. The vocabulary.
Each of those left-hand questions has a right answer that depends entirely on the right-hand answer, and only you have it. Handing you the left-hand version looks like consultation and is actually an abdication.
You answer in one sentence. The agent picks the mechanism, names it in brackets so the word starts to become yours, and writes it into the confirmation you approve:
You: They should see an error. Silently charging twice and hoping nobody notices is worse than an honest failure.
OUTER: Understood. The second tap will be rejected with a visible message rather than absorbed, and the first charge stands. The payment call gets a key derived from the cart so a repeat within the window returns the original result instead of creating a second charge [idempotency key]. Cost: a genuine second purchase of the same cart within 60 seconds needs a fresh cart. That is listed under Trade-offs below.
You never picked a library. You never heard the word "idempotent" before it had already been explained by the thing it does.
You are building with an AI agent. You type "fix the login bug". Thirty seconds later it has edited nine files, switched a branch, run a migration, and told you it is done.
Three questions you could not answer while it was happening:
- Which branch was it on?
- What else did those nine files touch?
- If this is wrong, how do you get back?
The agent was not being reckless. It was being fast, and nobody told it to stop and check. Speed is the feature until the day it is the bug.
OUTER inserts one step before the first file changes. The agent works out what it is about to do, writes it down in ordinary language, and waits.
flowchart LR
A["You: fix the login bug"] --> B{OUTER}
B --> C["Reads the code<br/>read-only"]
C --> D["Asks only the questions<br/>that change the outcome"]
D --> E["Shows one confirmation:<br/>branch, files, blast radius,<br/>how to undo"]
E --> F["Waits for your phrase"]
F --> G["Now it writes"]
style B fill:#1f2937,color:#fff
style F fill:#b45309,color:#fff
style G fill:#166534,color:#fff
Before anything changes, you get one block like this:
Change: Fix session expiry so users are not logged out mid-form
Repository: my-app (worktree: ~/code/my-app)
Branch: fix/session-expiry (based on main @ 4c1e9a2)
Environment: local only
Allowed changes: edit files, run tests
Allowed paths: src/auth/, src/hooks/useSession.ts
Allowed targets: none
Excluded: all-other-mutations, no migrations, no deploy, no push
Boundary: the login screen and the token refresh are untouched
Blast radius: anyone already signed in keeps their session; new sessions
get a 30-minute idle window instead of 10
Verification: a failing test written first, then the same test passing
Recovery: git restore the two files, or delete the branch
Reply exactly: APPROVE OUTER-7K2M9QX4
Until you type APPROVE OUTER-7K2M9QX4, the agent's normal route to that file is closed. A hook sits in front of every tool call and returns a hard refusal, not a suggestion.
Be clear about what "closed" means here, because a security promise you cannot check is worth nothing.
The gate works from a list of dangerous shapes. It refuses those and lets everything else through. That design is from 2026 and has not been widened since, so here is exactly where it holds and where it does not. Every row below was tested by running the hook, not by reading it.
| An agent tries | The gate |
|---|---|
| Writing or editing a source file | refuses |
git push, git commit, merge, deploy |
refuses |
Installs, builds, rm, destructive shell commands |
refuses |
node -e or python3 -c that writes a file |
passes through |
Writing to ~/.claude/settings.json or to shell dotfiles |
passes through |
An MCP tool whose name lacks a known destructive verb, such as create_or_update_file |
passes through |
That third row from the bottom is the one to sit with. It means an agent that reaches for an interpreter one-liner instead of the Write tool is not stopped. And the settings.json row means the gate does not protect the file that switches it on.
So treat this as a firm stop on the routes an agent takes by default, and not as a sandbox. If you need a boundary that holds against everything, run your agent in a container and use OUTER for the conversation it forces before the work starts. Those two solve different problems and they compose well.
Because "yes" is cheap and you will click it while thinking about something else.
The phrase is generated fresh for each task. It is unique. To type it, you have to look at the block it appeared under. That is the whole trick: it costs you three seconds of attention at the exact moment attention is worth the most.
And these do not work:
| You type | What happens |
|---|---|
go ahead |
Treated as a new request, not consent |
yes, sure, do it |
Same. No approval recorded |
| The phrase from your last task | Rejected as stale |
| The right phrase, wrong branch | Rejected. The approval was bound to a branch |
If you change the branch, the target, the environment, or what you are actually asking for, the old phrase dies and you get a fresh confirmation. Consent does not leak from one task to the next.
flowchart TD
subgraph T1["Tier 1: the skill"]
A1["Install the skill"] --> B1["Agent follows the interview<br/>and shows the confirmation"]
B1 --> C1["It is a strong habit.<br/>The agent can still skip it."]
end
subgraph T2["Tier 2: the skill plus hooks"]
A2["Install the skill"] --> B2["Wire three hook scripts"]
B2 --> C2["The write is refused at the door,<br/>on the routes the gate covers."]
end
style C1 fill:#78350f,color:#fff
style C2 fill:#166534,color:#fff
Tier 1 is what npx skills add gives you. One command, works in Claude Code, Codex, Cursor, and the rest. The agent reads the skill and follows it. This is guidance with real teeth, but it is still guidance.
Tier 2 is the gate. Four scripts in hooks/ that you wire into your agent's settings yourself. Now the refusal is mechanical rather than a matter of the agent's cooperation, within the coverage described above. See hooks/README.md.
A skill installer cannot write hooks into your agent's config, and it should not be able to. That is why the one-command version is Tier 1 only. It is a property of how skills install, not a feature held back.
stateDiagram-v2
[*] --> ASSESSING: you ask for something
ASSESSING --> AWAITING_OK: confirmation shown
AWAITING_OK --> CONFIRMED: exact phrase typed
AWAITING_OK --> ASSESSING: scope changed, phrase dies
CONFIRMED --> ACTIVE: first write allowed
ACTIVE --> ASSESSING: work went outside the envelope
ACTIVE --> PROVED: evidence recorded
PROVED --> [*]
PROVED is the fussy one. Source inspection does not earn it. "Looks good" does not earn it. A stale test run from before the change does not earn it. The agent has to say what it actually ran and what it actually saw, and keep proof of different kinds separate: reading the code is not running it, and running it locally is not running it in production.
The skill, in one command:
npx skills add Shivak11/outer-engineering-loopThen start any task with /plain-language-engineering-loop in Claude Code, or just describe what you want and let the skill trigger itself.
The gate, if you want writes actually refused: follow hooks/README.md. It takes about five minutes and needs Node.js, which your agent already requires.
This repository is the free edition. It is the complete OUTER v3 design, MIT licensed, yours to fork and change. Nothing in it is crippled and nothing phones home.
Development did not stop in 2026. It moved into Super-outer, a proprietary edition that is in private release. The two share a philosophy and very little code.
The honest difference is not that one has more features. It is that the free edition treats every task the same way, and the commercial one does not.
| Free (this repository) | Super-outer (commercial) | |
|---|---|---|
| Depth of questioning | The same interview for every task | Reads the request first and picks depth. Stays quiet on small safe edits |
| Questions asked | A fixed sequence | A computed allowance for that specific task, checked before you see it |
| When approval is demanded | In front of ordinary code work | Reserved for a genuinely critical consequence: production delivery, secrets, protected data, protected history, a real transaction |
| Expertise applied | One general checklist | Selects only the specialists the work actually touches, and loads only their playbooks |
| House style | Not considered | Reads your repository's own design system, components, tokens, accessibility and journey rules before any outside convention |
| Hosts | Claude Code and Codex, hooks wired by hand | Claude Code, Codex and Cursor, one command, with verify and rollback |
| Enforcement | The denylist gate described above | A different design. It does not stand in front of ordinary work at all |
| Maintained | No. This is the design as released | Yes |
That last-but-one row is the real fork in the road, and it is worth understanding before you choose.
The free edition stops you at the door on every code change. That is a strong teacher and an honest speed bump, and for a lot of people it is exactly right, particularly while you are still learning what to ask for.
Super-outer went the other way. Standing in front of every edit trains people to approve without reading, which destroys the value of asking at all. So it stays out of the way on ordinary work and saves the interruption for the small number of moments where the consequence is genuinely severe. That is a harder thing to build and an easier thing to live with.
If the free edition is useful to you and you want the version that knows when to stay quiet, open an issue here and say so. It is in private release and there is no public sign-up yet.
- It will slow you down. That is the point, and it is a real cost. On a two-line CSS tweak it is overhead. The skill tries to stay quiet on small safe edits, but it will sometimes ask when you wish it had not.
- Tier 1 cannot enforce anything. An agent that decides to skip the skill will skip it. Only the hooks make refusal mechanical.
- The gate is a denylist, and denylists have holes. It blocks the shapes it recognises. An interpreter one-liner, a write to your agent's own settings file, or an MCP tool with an innocuous name will pass. The full table is above under Where the edges are. This is published rather than buried because a security tool that hides its coverage is worse than one that has gaps.
- The hooks are opinionated about what counts as dangerous. File writes, git operations, installs, builds, deploys, destructive external calls. Reading, searching, and scratch files pass through untouched.
[skip-outer]exists for when you genuinely do not want the interview. It records what you skipped and names the biggest unexamined risk in one sentence, then gets out of the way. It authorises only the exact actions you named.
skills/plain-language-engineering-loop/ the skill itself (Tier 1)
SKILL.md the full interview and contract
assets/ templates the agent fills in
references/ failure vocabulary, install notes
hooks/ the enforcement scripts (Tier 2)
outer-interview-trigger.mjs runs on every prompt you submit
outer-pre-mutation-gate.mjs refuses the write
outer-confirmation-stop.mjs binds approval to the task
outer-state.mjs shared state, hashes only
assets/outer-banner.svg the wordmark
The state files store hashes and short status words. They never store your prompt text, your file paths, your approval phrase in readable form, or anything about your code.
MIT. Take it, fork it, change it.