Durable contract-driven goal execution for Claude Code. A subagent judge gates completion against an explicit Definition of Done.
> /plugin marketplace add bonfire-systems/goalkeeper> /plugin install goalkeeper@goalkeeper
Repo: bonfire-systems/goalkeeper
What's inside
Real reject-cycle, 3 minutes wall-clock. Goalkeeper caught a
MAX_RUNTIME_MS = 9999 // TODOsentinel placeholder in a benchmark test where the validator passed both rounds. Round 1: validator green, judge reject on DoD #3 + #7 with a 3-step fix-list. Round 2: real threshold (500ms = 10x measured baseline) with justification, validator green, judge approve. Full transcript with quoted verdicts:docs/demo.md. Long-form writeup on dev.to.
OpenAI Codex /goal | Ralph loop | goalkeeper | |
|---|---|---|---|
| Durable goal across many turns | ✓ | ✓ | ✓ |
| Validator back-pressure | optional | core | core |
| Independent judge gate against an explicit Definition of Done | — | — | core |
Anti-placeholder rule (stubs / .todo / it.only auto-reject) | — | informal | enforced |
| Linear chains of role-specific goals with judge-gated handoff | — | — | /goal-chain |
| Auto-pauses after N consecutive rejections | — | — | 5 (configurable) |
| Append-only checkpoint log per goal | — | informal | enforced |
| Spec lives in | CLI prompt | PROMPT.md | contract.md (validated against JSON Schema) |
| Pre-existing validator failures distinguished from goal-caused | — | — | validator_baseline_* |
Inspired by OpenAI Codex /goal and Geoffrey Huntley's Ralph loop, with one key addition: the judge.
Codex /goal runs autonomously until a stop-condition is met. The Ralph loop runs while :; do cat PROMPT.md | claude-code; done and leans on validators (compile, test, lint) to back-pressure the model. Both work, but both have the same failure mode: a passing validator is not the same as a finished feature. Tests can pass on stubs. Linters can pass on .todos. Codex can declare victory the moment its stop-condition string matches.
goalkeeper adds a second gate. After your validator passes, a fresh subagent — independent context, no rationalizations from the executing agent — reviews the diff and the progress log against your written Definition of Done, and either approves or returns a structured fix-list. After 5 rejections it pauses and asks for human help.
Add the goalkeeper marketplace, then install the plugin:
# inside Claude Code
/plugin marketplace add bonfire-systems/goalkeeper
/plugin install goalkeeper@goalkeeper
Skills become available under the goalkeeper: namespace. Invoke as /goalkeeper:goal "<objective>", /goalkeeper:goal-prep, /goalkeeper:goal-judge, etc. (Most users alias the namespace away — see "Aliasing" below.)
If you primarily use goalkeeper and want shorter commands, add aliases to ~/.claude/settings.json:
{
"aliases": {
"/goal": "/goalkeeper:goal",
"/goal-prep": "/goalkeeper:goal-prep",
"/goal-pause": "/goalkeeper:goal-pause",
"/goal-resume": "/goalkeeper:goal-resume",
"/goal-clear": "/goalkeeper:goal-clear",
"/goal-judge": "/goalkeeper:goal-judge",
"/goal-chain": "/goalkeeper:goal-chain"
}
}
/goal-prep "Migrate the test suite from Jest to Vitest"
goalkeeper reads your repo, asks a few targeted questions (objective, definition-of-done, validator command, non-goals), writes .claude/goals/<slug>/contract.md, and offers to start. Once running, it:
log.md).Check status anytime:
/goal
| Command | What it does |
|---|---|
/goal "<objective>" | Start (or resume) a goal. Auto-routes to /goal-prep if no contract exists yet. |
/goal | Show status of the active goal: last checkpoint, validator state, rejection count. |
/goal-prep "<rough idea>" | Interactively draft a contract — the highest-leverage step. Surveys the repo and uses targeted questions to lock in a precise spec. |
/goal-pause | Pause without losing state ("I'll be right back" — keeps the active slot). Pending wakeups become no-ops. |
gk park [slug] --needs "<what>" | Park a goal on a human-gated blocker (credential, consent, decision). Frees the active slot so other goals run; gk status lists the parked queue with each goal's unblock instruction. The goal is parked, never the agent. |
/goal-resume | Resume a paused or parked goal (gk resume [slug]). If resuming with rejections on the clock, asks whether to reset the counter. Re-arms a waiting chain. |
/goal-clear | Stop and archive the goal to .claude/goals/_archive/. Files are moved, never deleted. |
/goal-judge | Run the judge advisorily on the active goal (no state change). Useful as a manual sanity check. |
/goal-chain "<file>" | Run a linear sequence of goals; the judge gates progression between them. |
A contract is a markdown file at .claude/goals/<slug>/contract.md with frontmatter:
---
slug: jest-to-vitest-migration
objective: Migrate the test suite from Jest to Vitest with no behavioral regressions and a measurable speed improvement.
non_goals:
- Do not rewrite test logic
- Do not change source code under src/
definition_of_done:
- All test files import from "vitest" instead of "@jest/globals"
- jest.config.* is removed; vitest.config.ts exists with equivalent coverage thresholds
- "pnpm test" runs the full suite under Vitest with 100% of previously-passing tests still passing
- Wall-clock test runtime improves by at least 20% vs the Jest baseline
validator:
command: pnpm test --run && pnpm exec node scripts/check-no-jest-refs.mjs
success: exit_zero
timeout_seconds: 1200
checkpoint_cadence: every 5 file edits OR every 20 minutes
max_rejections: 5
judge_mode: subagent
wakeup_seconds: 270
---
## Context
<freeform body — file pointers, constraints, hints, anti-placeholder reminders>
The body of the contract is your "PROMPT.md" — file pointers, constraints, hints. See examples/ for full contracts.
Schema: schemas/contract.schema.json.
.claude/goals/.exit_zero (default) or regex:<pattern> matched against stdout.subagent (default, gate-quality) or inline (cheap, advisory only).dist/, build/, coverage/, IDE files). Add per-repo noise like generated migrations or vendor trees.A chain is an ordered list of slugs. The judge gates progression — only after approval does the next goal start.
---
name: bonfire-bass-rust-port
---
1. port-dsp-core-to-rust
2. wire-up-ffi-shim
3. swap-cpp-for-rust-in-host
4. delete-cpp-tree
Run with:
/goal-chain ".claude/goals/chains/bonfire-bass.md"
Each slug must already have a contract at .claude/goals/<slug>/contract.md. Run /goal-prep for each before starting the chain.
Most non-trivial goals span multiple roles — backend changes, UI wiring, migrations, tests, docs. Goalkeeper deliberately keeps just two agent slots (executor + judge) and stays framework-agnostic about how you spawn specialists. There are two patterns for getting role-shaped work through goalkeeper, and the right choice depends on how decoupled the roles are.
Frame each role as its own contract with its own Definition of Done, validator, and judge. Compose them with /goal-chain. The judge gates progression: backend's judge has to approve before UI starts; UI's judge has to approve before migrations.
---
name: stripe-integration
---
1. stripe-api-routes
2. stripe-db-migration
3. stripe-checkout-ui
4. stripe-e2e-tests
Each slug has its own .claude/goals/<slug>/contract.md with a focused DoD ("Stripe webhook signature verification works", "checkout component handles 3DS challenge", etc.) and a focused validator (pnpm test packages/api, pnpm test packages/web, etc.). See examples/chain.md for a worked example.
Choose Pattern A when: roles are sequenceable. One role's work produces the artifacts the next role consumes. The dependency graph is linear or close to it. You want the judge to gate-keep at each role boundary so a sloppy backend can't silently corrupt the UI step.
Tradeoff: chains force upfront decomposition. You have to know the boundaries before you start. If the boundaries shift mid-flight, you have to clear the chain and re-plan.
specialists: orchestration (proposed for v0.2)For tightly-coupled cross-role work — same files, can't be sequenced — Pattern A creates artificial mid-flight pauses. Pattern B is the escape hatch: a single contract with a specialists: field that lists the roles available, plus role-specific system prompts. The executing agent is the orchestrator and dispatches subtasks to specialist subagents (Claude Code general-purpose agents with custom system prompts, your own subagent_type definitions, or MCP-based agents — goalkeeper doesn't ship specialist definitions).
specialists:
- role: backend
system_prompt: "You are a senior Node.js engineer. ..."
- role: ui
system_prompt: "You are a senior React engineer. ..."
The judge still gates final approval against the unified DoD; the executing agent's log records which specialist did what.
FAQ
goalkeeper is a Claude Code plugin with 8 hand-picked skills for development work, indexed on Flowy. Install it with the command on its page. It includes goal-chain, goal-clear, goal-judge. Its skills do not fire on their own yet. Request auto-invocation to have Flowy route them as you prompt. Free and open source.
Is this plugin yours?
Claim it with GitHubSubmit a pluginPromote it