Skip to content

/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

shell
$ npx -y skills add bonfire-systems/goalkeeper --skill goal --agent claude-code

How 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
How auto-invocation works

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.md
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>",
Read more
Read it on GitHub ↗

Showing the first part of this file.

Ships withgoalkeeper

Durable contract-driven goal execution for Claude Code. A subagent judge gates completion against an explicit Definition of Done.

Get the whole plugin, auto-invoked
Stats
12
Stars
0
Views
2
Forks
Maintained
Maintenance
Python
Language
MIT
License
1mo ago
Last commit
2mo ago
Created

Repo: bonfire-systems/goalkeeper