Skip to content

Gates (enforcement)

SpecForge’s differentiator is that human approval is enforced by a hook, not left to the model’s goodwill.

plugins/specforge/hooks/hooks.json wires four events to scripts (referenced with ${CLAUDE_PLUGIN_ROOT} because installed plugins run from a cache copy):

Event Script Purpose
PreToolUse (Edit/Write/MultiEdit/NotebookEdit) gate-check.sh Block code edits until spec+plan approved
UserPromptSubmit inject-state.sh Inject current feature + gate status as context
SessionStart bootstrap.sh Announce the workspace + gate at session start
Stop validate-artifacts.sh Non-blocking artifact consistency check

gate-check.sh reads the PreToolUse JSON and decides, in order:

  1. No specforge/ workspace? → allow (inert on unrelated projects).
  2. Editing a file inside specforge/? → allow (that’s how specs get written).
  3. fasttrack: on? → allow (audited escape hatch).
  4. Active feature’s specify and plan are approved? → allow (gate OPEN).
  5. Otherwisedeny with an explanation telling Claude to finish/approve the plan or run /specforge:quick.

It fails open on any parse ambiguity, so it never wrongly blocks you — enforcement kicks in only when it can positively confirm an unapproved code edit.

A skill can say “don’t code before approval,” but nothing stops a model from ignoring it. A PreToolUse hook is executed by the Claude Code harness and can return a hard deny — the edit never happens. That turns “AI proposes, human approves” from a hope into an invariant.

Scope: an honest workflow, not a hostile-agent defense

Section titled “Scope: an honest workflow, not a hostile-agent defense”

The gate is an in-repo control: its scripts live in files a coding agent with file-write access can, in principle, read and edit — an agent could disable the PreToolUse block in hooks.json, or edit the gate’s own scripts. .specforge/ (and the Copilot and Cursor bundles’ own .specforge/ directories) is intentionally always-editable, precisely so SpecForge can self-update. The git pre-commit backstop is what catches that regardless: a tampered or disabled hook that still lets an unapproved edit through gets caught the moment the change is staged, no matter which agent (or human) made it. The model here is keep an honest workflow honest, not defend against an actively adversarial agent.

The gate isn’t just asserted — it’s verified in CI. The repo ships 28 tests, including the full deny → approve → allow → fast-track transition and fail-open behavior. See the test suite.

/specforge:init
# ask Claude to edit a source file → DENIED
/specforge:specify "…" → /specforge:plan → /specforge:approve specify → /specforge:approve plan
# the same edit is now ALLOWED