Undercut for Goose
Undercut is a free, MIT-licensed SKILL.md in the exact shape Goose's own docs specify for its Skills platform extension — cheap tier first, escalate only on evidence, never on a vibe. Drop it in .agents/skills/ and Goose advertises it at session start, then loads the full policy when a task matches. See below for exactly how.
How this maps onto Goose
Goose is Block's open source, on-machine AI agent (desktop app, CLI, and API, built in Rust), and it is now stewarded by the Agentic AI Foundation (AAIF) at the Linux Foundation. Per Goose's current Agent Skills docs, Skills run on "the built-in Skills platform extension, which is enabled by default" — no extra install to turn the mechanism on. A skill is a named subdirectory holding a SKILL.md with YAML frontmatter (name and description) followed by the instructions themselves — the same shape the open Agent Skills spec defines, and the same docs note goose skills are compatible with Claude Desktop and other adopters of that spec.
Discovery is scoped, in order: ~/.agents/skills/ for skills available in every session, .agents/skills/ for the current project, and ~/.agents/plugins/<plugin-name>/ for skills bundled with an installed plugin. The docs also list backward-compatible legacy locations — .goose/skills/, .claude/skills/, and ~/.claude/skills/ — but say plainly that .agents/skills/ "is the recommended standard" for anything new. Loading itself is progressive: at session start, "goose adds discovered skill names and descriptions to its instructions," and loads a skill's full body only when a request clearly matches its purpose, when you explicitly ask for it by name, or via goose skills list and the CLI's /skills <name> command.
Goose keeps Skills separate from its older, simpler context mechanism: .goosehints, a natural-language file for "general preferences, project context, and repeated instructions." A global .goosehints lives at ~/.config/goose/.goosehints and applies to every session; local ones sit in a project's directory hierarchy, load from the working directory up to the repo root at session start, and combine with hierarchical precedence — local overrides global on conflict. By default Goose also looks for AGENTS.md alongside .goosehints at each level (configurable via the CONTEXT_FILE_NAMES environment variable), and unlike a skill, every line of a loaded hints/context file is sent with every request rather than loaded on demand — the docs' own guidance is that a repeatable, multi-step workflow belongs in a 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 specifically here: Goose is bring-your-own-model across 15+ providers (Anthropic, OpenAI, Google, Ollama, OpenRouter, Azure, Bedrock, and more, per Goose's own README), 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 Goose and whichever LLM backend you've configured; it's a policy file Goose's own Skills extension reads at dispatch time, no proxy, no network hop.
Install for Goose
Clone or download undercutsh/firstpass, then copy skills/firstpass/ into .agents/skills/firstpass/ at your project root — Goose's own documented, recommended-standard location, scoped to this project.
Same directory shape, dropped under your home directory instead — Goose's documented global scope, available in every session regardless of which project you're in.
skills.sh lists Goose as a supported install target and shows this exact command run from your project root, but its own page doesn't publish which directory it writes into. Goose's docs above give the confirmed answer for a manual copy — if you want certainty rather than trusting the CLI's target, use Option 1.
Already have skills for Claude Code? Goose reads .claude/skills/ and ~/.claude/skills/ for backward compatibility — if .claude/skills/firstpass/ is already in the repo, Goose should discover it with nothing extra to install. The docs call .agents/skills/ the recommended standard for anything new, though, so prefer Option 1 for a fresh install.
Not the same as .goosehints. Goose's own docs describe .goosehints as best for short, always-on preferences — every line ships with every request. A multi-step routing policy like this one is exactly what the docs say belongs in a skill instead, loaded on demand rather than paying tokens on every turn.
testing/README.md and the raw JSON in testing/results/node src/main.js --mockgoose skills list and confirm firstpass shows up, then start a task and check whether Goose names it before mechanical work beginsZero lock-in. It's a SKILL.md Goose's own Skills extension 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 — Goose scores the flags itself (measured robust to getting them wrong, see above). It also doesn't pick a model for you: Goose is bring-your-own-model across 15+ providers, so resolving each tier to a concrete model is still a choice you make in Goose's own provider settings. Whether Goose actually loads 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 pulled in. skills.sh lists Goose and shows the same generic install command referenced above, but its page doesn't publish which directory the CLI writes to for Goose specifically; we've independently confirmed the manual-copy path (Option 1) directly against Goose's own docs, so use that if you want certainty rather than trusting the CLI's target. 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 Goose is currently doing with mechanical work.