/goal
Set or check status of a durable goalkeeper goal. Use this skill when the user invokes /goal "<objective>" to start a new contract-driven goal, or /goal with no arguments to see status of the currently active goal. Goalkeeper goals run autonomously across many turns with
$ npx -y skills add bonfire-systems/goalkeeper --skill goal --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.
- You can call itInvoke it directly when you want it.
- Slash command
/goal
Context preview
The summary Claude sees to decide when to auto-load this skill.
Set or check status of a durable goalkeeper goal. Use this skill when the user invokes /goal "<objective>" to start a new contract-driven goal, or /goal with no arguments to see status of the currently active goal. Goalkeeper goals run autonomously across many turns with
SKILL.md
goal.SKILL.mdname: goal
description: Set or check status of a durable goalkeeper goal. Use this skill when the user invokes /goal "<objective>" to start a new contract-driven goal, or /goal with no arguments to see status of the currently active goal. Goalkeeper goals run autonomously across many turns with checkpoint validation and judge-gated completion.
You are operating the **goalkeeper** skill — durable, contract-driven goal execution with judge-gated completion. This skill is invoked when the user runs `/goal` or `/goal "<objective>"`.
Execution modes (v0.3+)
Goalkeeper goals run in one of two execution modes depending on how the goal was activated:
Inline mode (standalone `/goal` / `/goal "<objective>"`)
Main conversation context runs the full Execution Loop (do work → checkpoint → run validator → invoke judge → branch on verdict). Use ScheduleWakeup to pace iterations across long-running validators. This is the default when the user invokes `/goal` directly.
Subagent mode (chain-driven via `/goal-chain`)
When `/goal-chain` is the orchestrator, the chain spawns a **fresh-context executor subagent** per goal. Main context only orchestrates — it does NOT do the per-goal implementation work. The executor subagent reads contract + log, does the work end-to-end, runs validator, and returns a structured summary. Main context then spawns the judge subagent, applies the verdict, and either advances the cursor or re-spawns the executor with the judge's fix-list.
This is the load-bearing change in v0.3 that lets multi-goal chains run autonomously without main context aging out. Main context cost per goal drops from "all the work + judge spawn" (tens of thousands of tokens accumulating per goal) to "executor return + judge spawn" (~10K tokens per goal, flat). A 9-goal chain that previously needed 9 fresh sessions can now complete in one main-context session.
The protocol described in the Execution Loop section below applies in BOTH modes — the difference is who runs it:
- Inline mode: main conversation runs the loop, with ScheduleWakeup pacing.
- Subagent mode: each goal's executor subagent runs the loop in its own context, with no ScheduleWakeup needed (the subagent runs end-to-end then returns).
Both modes use the same state.json / log.md / active.json files. The only mode-specific divergence is whether the judge is invoked by the loop (inline) or by the chain orchestrator after executor return (subagent). See `goal-chain/SKILL.md` Steps 6–8 for the subagent-mode orchestration details.
Canonical state shapes
Single source of truth for the JSON files goalkeeper reads and writes. Other skills (goal-clear, goal-judge, goal-chain) MUST conform to these shapes — drift here is the source of cross-skill bugs.
`.claude/goals/active.json`
Two shapes only — active or terminal.
**Active** (a goal is currently running):
{
"slug": "<slug>",
"activated_at": "<ISO8601>",
"chain": "<chain-name>"
}The `chain` field is OPTIONAL — present only when activation was driven by `/goal-chain` (start mode or advance mode). Standalone goals omit it.
**Terminal** (no active goal):
{
"slug": null,
"ended_at": "<ISO8601>",
"ended_reason": "done" | "cleared" | "chain_completed" | "aborted",
"previous_slug": "<slug>",
"previous_chain": "<chain-name>"
}`slug`, `ended_at`, and `ended_reason` are REQUIRED on terminal. `previous_slug` SHOULD be set when the last activity was a single goal or chain link. `previous_chain` SHOULD be set when a chain just ended (`chain_completed` or `aborted`).
`.claude/goals/<slug>/state.json`
{
"status": "active" | "paused" | "done" | "needs_human",
"rejection_count": <int>,
"started_at": "<ISO8601>",
"started_at_commit": "<git rev-parse HEAD or null>",
"started_at_dirty_paths": ["<paths from git status --porcelain at activation>"],
"chain_step": <int>,
"last_checkpoint_at": "<ISO8601 or null>",
"last_validator_result": "pass" | "fail: <reason>" | null,
"last_judge_verdict": "approve" | "reject" | null,
"approved_at": "<ISO8601>",
"paused_at": "<ISO8601>",
"resumed_at": "<ISO8601>",
"needs_human_at": "<ISO8601>",
"validator_baseline_result": "pass" | "fail" | "not_runnable" | null,
"validator_baseline_failing_paths": ["<path>"]
}`status`, `rejection_count`, `started_at`, `started_at_commit`, `started_at_dirty_paths` are REQUIRED on activation. `chain_step` SHOULD be present when the goal is part of a chain (denormalized for log clarity; chain.json is the source of truth for cursor). Timestamp fields populate as the goal transitions: `approved_at` on judge approve, `paused_at`/`resumed_at` on `/goal-pause`/`/goal-resume`, and `needs_human_at` when `rejection_count` reaches `max_rejections` and status flips to `needs_human`.
`validator_baseline_result` and `validator_baseline_failing_paths` are populated by `/goal-prep` if it ran the validator once at activation baseline (which prep already does to confirm the command is runnable). When set, the judge treats `failing_paths` as pre-existing — a goal-end validator failure on those same paths is not the goal's fault. When null (validator was not run at prep, or prep is skipped), the judge has no pre-existing baseline to subtract from and treats validator failures as goal-caused.
`.claude/goals/chain.json`
{
"name": "<chain name>",
"slugs": ["<slug>", "..."],
"cursor": <int>,
"status": "active" | "done" | "aborted",
"started_at": "<ISO8601>",
"completed_at": "<ISO8601 or null>",
"source_file": "<absolute path>",
"link_approvals": [
{"slug": "<slug>", "approved_at": "<ISO8601>"}
]
}`link_approvals` accumulates one entry per judge-approved link. Provides chain-level visibility independent of per-link state.json files.
`.claude/mission.json` (v0.2 — supervisor layer)
{
"name": "<from mission.md heading>",
"status": "active" | "done" | "escalated",
"started_at": "<ISO8601>",Read more
name: goal description: Set or check status of a durable goalkeeper goal. Use this skill when the user invokes /goal "<objective>" to start a new contract-driven goal, or /goal with no arguments to see status of the currently active goal. Goalkeeper goals run autonomously across many turns with checkpoint validation and judge-gated completion.
You are operating the **goalkeeper** skill — durable, contract-driven goal execution with judge-gated completion. This skill is invoked when the user runs `/goal` or `/goal "<objective>"`.
Execution modes (v0.3+)
Goalkeeper goals run in one of two execution modes depending on how the goal was activated:
Inline mode (standalone `/goal` / `/goal "<objective>"`)
Main conversation context runs the full Execution Loop (do work → checkpoint → run validator → invoke judge → branch on verdict). Use ScheduleWakeup to pace iterations across long-running validators. This is the default when the user invokes `/goal` directly.
Subagent mode (chain-driven via `/goal-chain`)
When `/goal-chain` is the orchestrator, the chain spawns a **fresh-context executor subagent** per goal. Main context only orchestrates — it does NOT do the per-goal implementation work. The executor subagent reads contract + log, does the work end-to-end, runs validator, and returns a structured summary. Main context then spawns the judge subagent, applies the verdict, and either advances the cursor or re-spawns the executor with the judge's fix-list.
This is the load-bearing change in v0.3 that lets multi-goal chains run autonomously without main context aging out. Main context cost per goal drops from "all the work + judge spawn" (tens of thousands of tokens accumulating per goal) to "executor return + judge spawn" (~10K tokens per goal, flat). A 9-goal chain that previously needed 9 fresh sessions can now complete in one main-context session.
The protocol described in the Execution Loop section below applies in BOTH modes — the difference is who runs it:
- Inline mode: main conversation runs the loop, with ScheduleWakeup pacing.
- Subagent mode: each goal's executor subagent runs the loop in its own context, with no ScheduleWakeup needed (the subagent runs end-to-end then returns).
Both modes use the same state.json / log.md / active.json files. The only mode-specific divergence is whether the judge is invoked by the loop (inline) or by the chain orchestrator after executor return (subagent). See `goal-chain/SKILL.md` Steps 6–8 for the subagent-mode orchestration details.
Canonical state shapes
Single source of truth for the JSON files goalkeeper reads and writes. Other skills (goal-clear, goal-judge, goal-chain) MUST conform to these shapes — drift here is the source of cross-skill bugs.
`.claude/goals/active.json`
Two shapes only — active or terminal.
**Active** (a goal is currently running):
{
"slug": "<slug>",
"activated_at": "<ISO8601>",
"chain": "<chain-name>"
}The `chain` field is OPTIONAL — present only when activation was driven by `/goal-chain` (start mode or advance mode). Standalone goals omit it.
**Terminal** (no active goal):
{
"slug": null,
"ended_at": "<ISO8601>",
"ended_reason": "done" | "cleared" | "chain_completed" | "aborted",
"previous_slug": "<slug>",
"previous_chain": "<chain-name>"
}`slug`, `ended_at`, and `ended_reason` are REQUIRED on terminal. `previous_slug` SHOULD be set when the last activity was a single goal or chain link. `previous_chain` SHOULD be set when a chain just ended (`chain_completed` or `aborted`).
`.claude/goals/<slug>/state.json`
{
"status": "active" | "paused" | "done" | "needs_human",
"rejection_count": <int>,
"started_at": "<ISO8601>",
"started_at_commit": "<git rev-parse HEAD or null>",
"started_at_dirty_paths": ["<paths from git status --porcelain at activation>"],
"chain_step": <int>,
"last_checkpoint_at": "<ISO8601 or null>",
"last_validator_result": "pass" | "fail: <reason>" | null,
"last_judge_verdict": "approve" | "reject" | null,
"approved_at": "<ISO8601>",
"paused_at": "<ISO8601>",
"resumed_at": "<ISO8601>",
"needs_human_at": "<ISO8601>",
"validator_baseline_result": "pass" | "fail" | "not_runnable" | null,
"validator_baseline_failing_paths": ["<path>"]
}`status`, `rejection_count`, `started_at`, `started_at_commit`, `started_at_dirty_paths` are REQUIRED on activation. `chain_step` SHOULD be present when the goal is part of a chain (denormalized for log clarity; chain.json is the source of truth for cursor). Timestamp fields populate as the goal transitions: `approved_at` on judge approve, `paused_at`/`resumed_at` on `/goal-pause`/`/goal-resume`, and `needs_human_at` when `rejection_count` reaches `max_rejections` and status flips to `needs_human`.
`validator_baseline_result` and `validator_baseline_failing_paths` are populated by `/goal-prep` if it ran the validator once at activation baseline (which prep already does to confirm the command is runnable). When set, the judge treats `failing_paths` as pre-existing — a goal-end validator failure on those same paths is not the goal's fault. When null (validator was not run at prep, or prep is skipped), the judge has no pre-existing baseline to subtract from and treats validator failures as goal-caused.
`.claude/goals/chain.json`
{
"name": "<chain name>",
"slugs": ["<slug>", "..."],
"cursor": <int>,
"status": "active" | "done" | "aborted",
"started_at": "<ISO8601>",
"completed_at": "<ISO8601 or null>",
"source_file": "<absolute path>",
"link_approvals": [
{"slug": "<slug>", "approved_at": "<ISO8601>"}
]
}`link_approvals` accumulates one entry per judge-approved link. Provides chain-level visibility independent of per-link state.json files.
`.claude/mission.json` (v0.2 — supervisor layer)
{
"name": "<from mission.md heading>",
"status": "active" | "done" | "escalated",
"started_at": "<ISO8601>",Showing the first part of this file.
Durable contract-driven goal execution for Claude Code. A subagent judge gates completion against an explicit Definition of Done.
Repo: bonfire-systems/goalkeeper
Other skills on goalkeeper.
- /goal-chain
Run a linear sequence of goalkeeper goals where the judge gates progression between them. Use when the user invokes /goal-chain "<file>" to start a chain. Also auto-invoked by the judge skill on approval to advance the chain cursor.
Open skill - /goal-clear
Stop and archive the currently active goalkeeper goal. Use when the user invokes /goal-clear to abandon or finalize a goal. Files are moved to .claude/goals/_archive/, never deleted.
Open skill - /goal-judge
The gate. Reviews the active goalkeeper goal against its definition-of-done and either approves (advance / mark done) or rejects (with a structured fix-list). Auto-fired by the goal skill when the validator passes (inline mode), or invoked by the goal-chain orchestrator after
Open skill - /goal-pause
Pause the currently active goalkeeper goal without losing state. Use when the user invokes /goal-pause. The goal can later be resumed with /goal-resume.
Open skill - /goal-prep
Interactively draft a goalkeeper contract before executing. Use this skill when the user invokes /goal-prep "<rough idea>", or when /goal is called for a slug that has no contract yet. Produces a well-formed contract.md with explicit objective, definition-of-done, validator
Open skill - /goal-resume
Resume a paused or needs_human goalkeeper goal. Use when the user invokes /goal-resume after they've manually unblocked the goal (e.g. fixed a problem the judge flagged).
Open skill

