Skip to content

About

OUTER: Outcome, Unacceptable, Trade-offs, Evidence, Recovery. Five questions your coding agent answers in plain English, before it changes a single file.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

9 Commits

Folders and files

Repository files navigation

OUTER: Outcome, Unacceptable, Trade-offs, Evidence, Recovery

OUTER

Outcome  ·  Unacceptable  ·  Trade-offs  ·  Evidence  ·  Recovery

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-loop

The five questions

Every 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.


The same question, asked two ways

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.

And then it tells you what it picked

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.

The thing this fixes

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:

  1. Which branch was it on?
  2. What else did those nine files touch?
  3. 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
Loading

What you actually see

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.

Where the edges are

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.


Why a typed phrase and not a yes button

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.


The two ways to run it

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
Loading

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.


The lifecycle

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 --> [*]
Loading

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.


Install

The skill, in one command:

npx skills add Shivak11/outer-engineering-loop

Then 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.


Free and premium

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.

Honest limits

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

Repository layout

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.


Licence

MIT. Take it, fork it, change it.

About

OUTER: Outcome, Unacceptable, Trade-offs, Evidence, Recovery. Five questions your coding agent answers in plain English, before it changes a single file.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages