Undercut for OpenHands
Undercut is a free, MIT-licensed policy file in the same SKILL.md layout OpenHands' own docs specify — cheap tier first, escalate only on evidence, never on a vibe. Drop it in .agents/skills/ and OpenHands advertises it, then loads it in full when a task matches. See below for exactly how.
How this maps onto OpenHands
Per OpenHands' current Skills Overview docs, OpenHands (formerly OpenDevin) implements the open Agent Skills specification and layers two of its own extensions on top: keyword triggers and file-path paths. A skill is a directory named for it, containing a required SKILL.md with frontmatter — name must match the directory, description tells the agent when it applies. OpenHands' documented locations are <project>/.agents/skills/<skill-name>/SKILL.md for that project's workspace, ~/.agents/skills/ for every conversation you run, and a public registry OpenHands maintains at OpenHands/extensions. The legacy .openhands/skills/ and .openhands/microagents/ directories — the old "microagent" naming from before the Agent Skills convergence — still work, but the docs say to use .agents/skills/ for anything new.
Loading is progressive by design: a skill's name and description are advertised to the model up front, and the full SKILL.md body is only injected when a task matches — keeping the initial prompt small rather than front-loading every skill in full. OpenHands' two extensions add deterministic paths around that: a triggers list injects the skill's full content the moment a keyword shows up in a user message (the skill stays callable by the model too), while a paths list turns the file into a rule that isn't advertised to the model at all and fires once per conversation the first time a matching file is read, edited, or created — and per the same docs, if a file declares both, paths wins. Separate from all of this, OpenHands also reads a repository-root AGENTS.md (plus recognizes CLAUDE.md/GEMINI.md) as always-on context loaded in full into the initial system prompt — the docs' own guidance is to keep that short and push anything specialized into an on-demand skill instead, which is exactly the shape Undercut ships in.
Undercut's tier names — cheap, standard, frontier, apex — stay generic on purpose, and that matters here specifically: OpenHands is bring-your-own-model, configurable against Anthropic, OpenAI, and dozens of other providers through LiteLLM, with no single house model to hard-code a mapping onto. models.md gives a rule of thumb instead — cheap resolves to the smallest/fastest model on your configured roster, apex to a single batched tie-break call on whichever model you'd trust most with the hardest 1% of the work. Nothing here sits between OpenHands and whichever LLM backend you've configured; it's a policy file OpenHands' own skill loader reads at dispatch time, no proxy, no network hop.
Install for OpenHands
Clone or download undercutsh/firstpass, then copy skills/firstpass/ into OpenHands' documented .agents/skills/ location at your project root. Run this from your repo root once you've cloned the source alongside it.
Same directory shape, dropped under your home directory instead — OpenHands' docs list this as the user-scope location, applied to every conversation you run rather than one project workspace.
No npx skills add path yet. skills.sh doesn't list OpenHands as a supported install target as of this writing — the manual copy above, straight into OpenHands' own documented .agents/skills/ path, is the confirmed way in for now.
testing/README.md and the raw JSON in testing/results/node src/main.js --mockfirstpass before starting mechanical workZero lock-in. It's a SKILL.md OpenHands' own skill loader reads — delete the directory and you're back to status quo.
How it works
The Undercut Rubric · v1This is a named, versioned spec — not internal logic we could quietly change. When the rubric changes (a new flag, a different cap), the version number moves and the changelog says why.
Every unit of work gets scored against six flags, then assigned the cheapest tier that can plausibly handle it. It only moves up a tier when something objective proves it needs to — a failed check, a measured disagreement, an explicit uncertainty flag. It never moves up on a guess, and it never moves back down mid-task.
0 flags → cheap · 1–2 → standard · 3+ or any ownership/judgment call → frontier. Apex only when you can write one sentence stating why the marginal intelligence pays for the cost.
Cheap-to-verify ⇒ cheap-to-generate. If output can be checked mechanically, it starts at the lowest tier regardless of how hard it looks — verification catches failure. Only unverifiable work needs to start high.
The next tier up gets only the failed or uncertain items, plus verification notes. It resolves the residue — it never redoes the batch.
Never de-escalate mid-task. Max one retry per tier. Anything still unresolved goes to a single batched apex tie-break call. One hard file in a 700-file job doesn't drag the other 699 up a tier.
unit of work
|
6-flag rubric
|
+---+----+
| 0 flags|--> cheap
| 1-2 |--> standard
| 3+/own |--> frontier
+---+----+
| fail x2 / disagree / uncertain
v
escalate +1 tier
(residue only)
|
still unresolved?
v
single batched
apex tie-break
Why it holds up
Even when the rubric flags are scored imperfectly, the escalator still routes work to the right tier almost every time — because a wrong flag only ever costs one extra cheap attempt, never a wrong answer or a big bill.
A stock dispatcher model reproduces the rubric flags imperfectly — Haiku hits 90% agreement, Sonnet 93%. The weakest flag (format-strict) is only 60%. None of that matters: under the shipped policy, both dispatchers still route 100% of units to the correct tier. A wrong flag changes the ladder cap, never the base tier — worst case, one extra cheap attempt.
Every rule is a property of the unit of work — verifiable, ambiguous, blast-radius — never a specific model's known weakness. Vendor cards go stale as models change; work properties don't. It's also hard-won — a format-strict rule that worked on Anthropic backfired on Gemini, which is exactly why the rubric no longer encodes per-vendor behavior.
Honest limits
Not a proxy, not a gateway, doesn't auto-flag in production — OpenHands scores the flags itself (measured robust to getting them wrong, see above). It also doesn't pick a model for you: OpenHands is bring-your-own-model across many providers, so resolving each tier to a concrete model is still a choice you make in OpenHands' own LLM settings. Whether the CLI or OpenHands Cloud actually invokes a matching skill on a given task is model judgment, same as any other progressive-disclosure system — a description that doesn't clearly match the work won't get loaded. The directory paths, precedence rules, and trigger/path behavior described above come from OpenHands' own current Skills docs; we haven't independently re-tested every edge case end-to-end ourselves. Doesn't promise a dollar figure for your workload, and doesn't prove your number until you run it — every figure here is a reproducible observation from our tasks and graders, not a guarantee about yours. Full list of general limits, with the finance/procurement note and the paid-tier disclosure, is on the main page.
Nothing about this page expires. What does keep happening is the token bill, one session at a time, on whatever OpenHands is currently doing with mechanical work.