Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/docs/GOAL_PROTOCOL.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ The hook fires when a worker tries to terminate (report done). It looks up the a
- `signed_off: true` → release.
- Anything else → block. Worker must either fix the underlying failure and re-verify, or hand off to the operator with the kickback reason.

A reference hook config ships at `.claude/settings.example.json`. Copy it into `.claude/settings.json` (or merge into existing) and adjust paths if your harness exposes task-id differently.
A reference hook config ships at `.claude/settings.example.json`. The installer also ships `.claude/hooks/goal-gate.sh` as the real executable gate. Copy the settings example into `.claude/settings.json` (or merge into existing), ensure `jq` is installed, and adjust task-id resolution if your harness exposes it differently.

## Kickback routing

Expand Down
37 changes: 37 additions & 0 deletions .claude/hooks/goal-gate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/env bash
# goal-gate.sh — Stop-hook gate for claude-layers /goal contracts.
#
# Blocks worker completion when CLAUDE_GOAL_ID points to a goal whose proof is
# missing or whose signed_off value is not the literal JSON boolean true.

set -euo pipefail

TASK_ID="${CLAUDE_GOAL_ID:-}"

if [ -z "$TASK_ID" ]; then
echo '[goal-gate] no CLAUDE_GOAL_ID set; releasing (no contract to enforce).' >&2
exit 0
fi

PROOF=".claude/goals/${TASK_ID}.proof.json"

if [ ! -f "$PROOF" ]; then
echo "[goal-gate] BLOCK: no proof at $PROOF. Run /goal-verify $TASK_ID first." >&2
exit 2
fi

if ! command -v jq >/dev/null 2>&1; then
echo '[goal-gate] BLOCK: jq is required to read the proof artifact.' >&2
exit 2
fi

SIGNED="$(jq -r '.signed_off' "$PROOF")"

if [ "$SIGNED" = "true" ]; then
echo "[goal-gate] RELEASE: $TASK_ID signed off." >&2
exit 0
fi

REASON="$(jq -r '.kickback_reason // "unspecified"' "$PROOF")"
echo "[goal-gate] BLOCK: signed_off=$SIGNED; reason: $REASON" >&2
exit 2
31 changes: 1 addition & 30 deletions .claude/settings.example.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"_comment": "Reference Stop-hook config for the /goal protocol. Copy into .claude/settings.json (or merge into existing) and adjust the task-id resolution if your harness exposes it differently. The hook blocks the worker from reporting done until .claude/goals/<task-id>.proof.json exists with signed_off === true.",
"_comment": "Reference Stop-hook config for the /goal protocol. Copy into .claude/settings.json (or merge into existing). The installer provides .claude/hooks/goal-gate.sh; adjust that script if your harness resolves task IDs differently. The hook requires jq and blocks worker completion until .claude/goals/<task-id>.proof.json exists with signed_off === true.",
"hooks": {
"Stop": [
{
Expand All @@ -12,34 +12,5 @@
]
}
]
},
"_hook_script_example": {
"_comment": "Save as .claude/hooks/goal-gate.sh and chmod +x. Resolves task-id from $CLAUDE_GOAL_ID (set by the operator or /goal command), reads the proof, blocks unless signed_off is the literal boolean true.",
"_script": [
"#!/usr/bin/env bash",
"set -euo pipefail",
"",
"TASK_ID=\"${CLAUDE_GOAL_ID:-}\"",
"if [ -z \"$TASK_ID\" ]; then",
" echo '[goal-gate] no CLAUDE_GOAL_ID set; releasing (no contract to enforce).' >&2",
" exit 0",
"fi",
"",
"PROOF=\".claude/goals/${TASK_ID}.proof.json\"",
"if [ ! -f \"$PROOF\" ]; then",
" echo \"[goal-gate] BLOCK: no proof at $PROOF. Run /goal-verify $TASK_ID first.\" >&2",
" exit 2",
"fi",
"",
"SIGNED=$(jq -r '.signed_off' \"$PROOF\")",
"if [ \"$SIGNED\" = \"true\" ]; then",
" echo \"[goal-gate] RELEASE: $TASK_ID signed off.\" >&2",
" exit 0",
"fi",
"",
"REASON=$(jq -r '.kickback_reason // \"unspecified\"' \"$PROOF\")",
"echo \"[goal-gate] BLOCK: signed_off=$SIGNED; reason: $REASON\" >&2",
"exit 2"
]
}
}
70 changes: 70 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: validate

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
shellcheck:
name: shellcheck installer
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run shellcheck
run: |
sudo apt-get update
sudo apt-get install -y shellcheck
shellcheck install.sh

install-test:
name: install into temp dir
runs-on: ubuntu-latest
strategy:
matrix:
mode: [full, minimal]
steps:
- uses: actions/checkout@v4
- name: Create target dir
run: mkdir -p /tmp/install-target
- name: Run installer (${{ matrix.mode }})
run: |
if [ "${{ matrix.mode }}" = "minimal" ]; then
./install.sh /tmp/install-target --minimal --yes
else
./install.sh /tmp/install-target --yes
fi
- name: Verify required files
run: |
test -f /tmp/install-target/CLAUDE.md
test -f /tmp/install-target/.claude/agents/FORGE.md
test -f /tmp/install-target/.claude/agents/QUILL.md
test -f /tmp/install-target/.claude/agents/SCOUT.md
test -f /tmp/install-target/.claude/agents/WARDEN.md
test -f /tmp/install-target/.claude/commands/goal.md
test -f /tmp/install-target/.claude/commands/goal-verify.md
test -d /tmp/install-target/.claude/goals
test -f /tmp/install-target/.claude/projects/PROJECT_TEMPLATE.md
test -f /tmp/install-target/.claude/orchestrator/ARGENT.md
test -f /tmp/install-target/.claude/hooks/goal-gate.sh
test -x /tmp/install-target/.claude/hooks/goal-gate.sh
test -f /tmp/install-target/.claude/docs/LAYERING_MODEL.md
test -f /tmp/install-target/.claude/docs/ROUTING_PATTERNS.md
test -f /tmp/install-target/.claude/docs/CREATING_OVERLAYS.md
test -f /tmp/install-target/.claude/docs/GOAL_PROTOCOL.md
test -f /tmp/install-target/.claude/settings.example.json
- name: Verify example projects (full only)
if: matrix.mode == 'full'
run: |
test -f /tmp/install-target/.claude/projects/PROJECT_WEBAPP.md
test -f /tmp/install-target/.claude/projects/PROJECT_API.md
test -f /tmp/install-target/.claude/projects/PROJECT_CLI.md
- name: Verify example projects absent (minimal only)
if: matrix.mode == 'minimal'
run: |
test ! -f /tmp/install-target/.claude/projects/PROJECT_WEBAPP.md
test ! -f /tmp/install-target/.claude/projects/PROJECT_API.md
test ! -f /tmp/install-target/.claude/projects/PROJECT_CLI.md
- name: Test idempotency
run: ./install.sh /tmp/install-target --yes
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@

All notable changes to claude-layers will be documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [Unreleased]

### Added

- Installed `.claude/hooks/goal-gate.sh` as a real Stop-hook gate instead of only documenting the script inside `settings.example.json`
- Active GitHub Actions workflow at `.github/workflows/validate.yml` for installer and hook checks
- README 30-second mental model for how `CLAUDE.md`, role overlays, project overlays, `/goal`, Warden, and `goal-gate.sh` fit together

### Changed

- Installer now copies `goal-gate.sh`, marks it executable, and reminds users that enabled Stop-hook gating requires `jq` and `CLAUDE_GOAL_ID`
- Validation now checks that the installed hook exists and is executable

## [1.1.0] - 2026-05-19

### Added
Expand Down
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ This precedence model is the whole point. It means you can write project overlay

## What gets installed

The 30-second model:

- `CLAUDE.md` tells the agent how to behave.
- Role overlays tell it what job it has.
- Project overlays tell it what matters here.
- `/goal` tells it what done means.
- Warden and `goal-gate.sh` keep it from claiming done without proof.

```
your-project/
├── CLAUDE.md # Layer 1: universal discipline
Expand All @@ -77,6 +85,8 @@ your-project/
│ └── PROJECT_CLI.md # example: command-line tool
├── orchestrator/
│ └── ARGENT.md # routing and context composition
├── hooks/
│ └── goal-gate.sh # Stop hook gate for /goal proofs
├── docs/
│ ├── LAYERING_MODEL.md # how layers compose at runtime
│ ├── ROUTING_PATTERNS.md # when to use which agent
Expand Down Expand Up @@ -131,9 +141,9 @@ The "what you do not own" sections in each overlay are deliberately heavier than

## The `/goal` contract (v1.1.0+)

Every code-changing slice gets a manifest at `.claude/goals/<task-id>.yaml` that declares its `done_when` checks (tests, commands, file constraints, diff constraints). Workers cannot self-assess "done" — Warden runs the manifest, writes `.claude/goals/<task-id>.proof.json`, and a Stop hook refuses to release the worker until `signed_off: true`. For Scout investigations and design slices where the verifier is operator judgment, Warden emits `signed_off: "pending"` plus a reviewer checklist; the operator flips it manually.
Every code-changing slice gets a manifest at `.claude/goals/<task-id>.yaml` that declares its `done_when` checks (tests, commands, file constraints, diff constraints). Workers cannot self-assess "done" — Warden runs the manifest, writes `.claude/goals/<task-id>.proof.json`, and the installed Stop hook at `.claude/hooks/goal-gate.sh` refuses to release the worker until `signed_off: true`. For Scout investigations and design slices where the verifier is operator judgment, Warden emits `signed_off: "pending"` plus a reviewer checklist; the operator flips it manually.

Full spec lives at `.claude/docs/GOAL_PROTOCOL.md`. Reference Stop-hook config at `.claude/settings.example.json`.
Full spec lives at `.claude/docs/GOAL_PROTOCOL.md`. Reference Stop-hook config at `.claude/settings.example.json`; copy or merge it into `.claude/settings.json` when you want gating enabled. The hook requires `jq` and a `CLAUDE_GOAL_ID` environment variable for sessions that should be gated.

## The orchestrator (optional but useful)

Expand Down
10 changes: 9 additions & 1 deletion install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ What gets installed:
.claude/projects/PROJECT_API.md Layer 3: example (full install only)
.claude/projects/PROJECT_CLI.md Layer 3: example (full install only)
.claude/orchestrator/ARGENT.md Orchestrator config
.claude/hooks/goal-gate.sh Stop hook gate for /goal proof enforcement
.claude/docs/LAYERING_MODEL.md How layers compose
.claude/docs/ROUTING_PATTERNS.md When to use which agent
.claude/docs/CREATING_OVERLAYS.md How to write a new overlay
Expand Down Expand Up @@ -203,6 +204,12 @@ echo ""
echo " [Orchestrator]"
copy_with_check "$SCRIPT_DIR/.claude/orchestrator/ARGENT.md" "$TARGET_DIR/.claude/orchestrator/ARGENT.md"

# Hooks
echo ""
echo " [Hooks]"
copy_with_check "$SCRIPT_DIR/.claude/hooks/goal-gate.sh" "$TARGET_DIR/.claude/hooks/goal-gate.sh"
chmod +x "$TARGET_DIR/.claude/hooks/goal-gate.sh" 2>/dev/null || true

# Docs
echo ""
echo " [Docs]"
Expand All @@ -227,7 +234,8 @@ fi
echo " 3. Copy PROJECT_TEMPLATE.md to PROJECT_<YOURNAME>.md for new projects"
echo " 4. Update path-to-project mapping in .claude/orchestrator/ARGENT.md"
echo " 5. Read .claude/docs/GOAL_PROTOCOL.md and decide whether to wire the Stop hook from .claude/settings.example.json"
echo " 6. Commit the .claude directory to your repo"
echo " 6. If you enable the Stop hook, ensure jq is installed and set CLAUDE_GOAL_ID for goal-scoped sessions"
echo " 7. Commit the .claude directory to your repo"
echo ""
echo "Quick test in Claude Code:"
echo " Read CLAUDE.md, then summarize in three sentences what behaviors it requires."
Expand Down
2 changes: 2 additions & 0 deletions validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ jobs:
test -d /tmp/install-target/.claude/goals
test -f /tmp/install-target/.claude/projects/PROJECT_TEMPLATE.md
test -f /tmp/install-target/.claude/orchestrator/ARGENT.md
test -f /tmp/install-target/.claude/hooks/goal-gate.sh
test -x /tmp/install-target/.claude/hooks/goal-gate.sh
test -f /tmp/install-target/.claude/docs/LAYERING_MODEL.md
test -f /tmp/install-target/.claude/docs/ROUTING_PATTERNS.md
test -f /tmp/install-target/.claude/docs/CREATING_OVERLAYS.md
Expand Down
Loading