Metadata-Version: 2.4
Name: specgate-cli
Version: 0.2.1
Summary: Turn a spec into a frozen acceptance harness enforced via Claude Code Stop hook.
Author-email: Prabhay <coolprabhay90@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/prabhay759/specgate
Project-URL: Repository, https://github.com/prabhay759/specgate
Project-URL: Bug Tracker, https://github.com/prabhay759/specgate/issues
Keywords: claude,claude-code,ai,agents,testing,acceptance-criteria,tdd,spec,hooks
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Testing
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# specgate

Turn a spec into a frozen acceptance harness, then enforce it as a Claude Code Stop hook — so the agent literally cannot end its turn until every criterion passes.

## Core invariant

**The agent that writes the code must never be able to alter the harness that grades it.**

The harness is generated and human-approved *before* implementation begins, then frozen by a SHA-256 hash. The Stop-hook gate re-verifies that hash on every run. A hash mismatch is a hard block — the agent cannot weaken its own test.

## Install

```sh
pipx install specgate
```

Requires Python 3.11+. No runtime dependencies.

## The generate → freeze → enforce flow

### 1. Init (once per repo)

```sh
cd my-repo
specgate init
```

Creates `.specgate/` and merges a Stop-hook entry into `.claude/settings.json`. Existing hooks are preserved.

### 2. Write a spec

Write a spec file with concrete, measurable requirements:

```markdown
# Feature: Password Reset

1. POST /password-reset with a valid email returns HTTP 202 and enqueues one email.
2. A reset token is single-use; a second attempt returns HTTP 400.
3. Tokens expire after exactly 24 hours.
```

Vague specs are rejected by `specgate generate`. Clarify first.

### 3. Generate the harness

```sh
specgate generate issues/password-reset.md
```

Calls `claude -p` to extract acceptance criteria and emit pytest check files into `.specgate/harness/`. Prints a summary of every criterion for review. `manifest.json` is written with `approved: false`.

### 4. Review and freeze (human sign-off)

Read the summary. Inspect the generated checks in `.specgate/harness/`. Edit them if needed. When satisfied:

```sh
specgate freeze
```

This computes a SHA-256 hash over the harness directory, stores it in `manifest.json`, and sets `approved: true`. From this point, the harness is the definition of done.

### 5. Implement

Tell the agent to implement the feature. The Stop hook fires at the end of every reply:

- If the harness passes → the turn ends normally.
- If any criterion fails → the agent receives a specific, actionable block reason naming the failing criteria and is forced to continue.
- If the hash has changed → hard block: the agent is told to restore the harness. It cannot bypass this.
- After 5 consecutive blocks → the agent is instructed to summarize its attempts and ask the human for guidance (before the 8-block Claude Code cap).

### Debug manually

```sh
specgate check     # same code path as the hook — prints pass/fail per criterion
specgate status    # show approval state, hash match, block count
```

## Gate protocol (Stop hook)

The hook receives JSON on stdin and writes to stdout:

- **Block:** `{"decision": "block", "reason": "<specific failing criteria + hint>"}`, exit 0.
- **Pass:** no output, exit 0.
- **Loop guard:** if `stop_hook_active` is true, exit 0 immediately — prevents infinite loops.

## `.specgate/` layout

```
.specgate/
  harness/          ← generated pytest checks (frozen by hash)
  manifest.json     ← criteria, check map, hash, approval flag
  state.json        ← block counter, last run (written by gate, never committed)
```

Commit `.specgate/harness/` and `.specgate/manifest.json`. Do not commit `state.json`.

## Configuration

| Env var | Default | Purpose |
|---|---|---|
| `SPECGATE_ESCALATION` | `5` | Block count before "ask the human" escalation |
| `SPECGATE_TIMEOUT` | `120` | Wall-clock budget (seconds) for the harness run |
| `SPECGATE_BACKEND_TIMEOUT` | `120` | Timeout for `claude -p` generation calls |

## Constraints

- Standard library only — no runtime dependencies.
- Generation backend: `claude -p` (Claude Code CLI) — reuses your existing auth.
- Generated harness may use the target repo's test framework (default: pytest).
