Semantic governance gate for spec files: classifies PR changes against a locked project goal and scope.
Project description
SpecGuard is a semantic governance layer for spec files. It classifies every PR change against your locked project goal and scope — passing additive changes silently, warning on low-confidence shifts, and blocking unapproved direction changes at merge time.
The Problem
In repos where AI agents and humans both contribute, a PR can look perfectly fine on the surface while silently shifting the project's direction. SpecGuard catches that — not by checking who made the change, but by understanding what the change means against your locked goal and scope.
PR: "refactored README for clarity"
Change: Added a full SaaS pricing section
to a project scoped as a local CLI tool.
SpecGuard: ❌ SCOPE CHANGE — 94% confidence
"SaaS pricing" is out of scope
requires approval from @architect
How It Works
Lock your goal and scope in .specguard/lock.json. SpecGuard does the rest on every PR.
PR opened
├─ Not a watched file ───────────────────── ✅ Pass
├─ Protected path, wrong author ──────────── ❌ Block (no AI involved)
└─ Watched spec file changed
└─ Claude classifies the diff
├─ ADDITIVE ───────────────────── ✅ Pass (silent)
├─ SCOPE CHANGE, low confidence ── ⚠️ Warn
└─ SCOPE CHANGE, high confidence ── ❌ Block (until authorized approval)
Approving via GitHub's normal review flow re-evaluates the check automatically — no new commits needed.
Quickstart
1. Create .specguard/lock.json
{
"goal": "A CLI tool that converts Markdown to PDF",
"scope_in": ["Markdown parsing", "PDF rendering", "CLI flags"],
"scope_out": ["GUI", "cloud sync", "collaboration features"]
}
2. Add the workflow
# .github/workflows/specguard.yml
name: specguard
on:
pull_request:
pull_request_review:
types: [submitted]
permissions:
contents: read
pull-requests: read
jobs:
specguard: # the required branch-protection check
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: {fetch-depth: 0} # required: base...head history
- uses: Sawaiz-zip/spec-guard@v0
with:
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
reevaluate: # an approval re-runs the check in place
if: github.event_name == 'pull_request_review' && github.event.review.state == 'approved'
runs-on: ubuntu-latest
permissions: {actions: write}
steps:
- env:
GH_TOKEN: ${{ github.token }}
run: |
run_id=$(gh api "repos/${{ github.repository }}/actions/workflows/specguard.yml/runs?event=pull_request&head_sha=${{ github.event.pull_request.head.sha }}" --jq '.workflow_runs[0].id // empty')
[ -n "$run_id" ] && gh api -X POST "repos/${{ github.repository }}/actions/runs/$run_id/rerun"
3. Set ANTHROPIC_API_KEY as a repo secret, then require the specguard check under branch protection.
That's it for solo use — scope changes now warn on every PR. To make them block until an authorized teammate approves, add roles:
# .specguard/roles.yml (optional — presence switches warn mode to enforce mode)
roles:
architect: [your-github-username]
rules:
".specguard/**": # nobody outside the role may touch the lock itself
edit: architect
"README.md": # who can approve scope changes per file
scope_changes: {approve: architect}
# .specguard/config.yml (optional — these are the defaults)
watch: ["README.md", "CLAUDE.md", "AGENTS.md", "ARCHITECTURE.md", "*.kilo", ".specguard/**"]
block_threshold: 0.75
on_error: warn # vendor outage: pass with a loud warning ("fail" to block)
model: claude-sonnet-4-6
max_diff_chars: 30000
You bring your own API key and choose the model — SpecGuard never bills you directly. Set
model:in.specguard/config.ymlto use any model you have access to. With the defaultclaude-sonnet-4-6expect roughly $0.01–0.02 per watched file per push (~3–5K input + ~500 output tokens); it scored a perfect confusion matrix on the calibration corpus. Note:claude-opus-4-8is hard-blocked by a project guardrail (no quality gain on this task at ~6× the cost).
Roadmap
| Phase | Status | What ships |
|---|---|---|
| 0 — CI Gate | 🔴 Building | GitHub Action · scope classification · role-based approval · branch protection |
| 1 — Local Tools | ⚪ Planned | CLI (specguard init, specguard check) · pre-commit hook · MCP server |
| 2 — GitHub App | ⚪ Planned | Native Checks API · fork PR support · bot vs human identity · Spec Kit adapter |
| 3 — Advanced | ⚪ Planned | Section-level locking · monorepo support · multi-provider classifier |
Principles
No false blocks. No new UI. No dashboards.
The only enforceable boundary is merge time — everything else is advisory. A wrong Friday block means uninstall by Monday, so additive changes always pass silently. Hard blocks are deterministic (no AI). Probabilistic verdicts always show their confidence and never block without explanation.
Full constitution: .specify/memory/constitution.md
Built with Spec Kit · Powered by Claude · MIT License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file specguard_ci-0.1.0.tar.gz.
File metadata
- Download URL: specguard_ci-0.1.0.tar.gz
- Upload date:
- Size: 531.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aacd812215cb01bc746e3c8072ac9e6126d62053e7939b5f06e1e6629f390cb9
|
|
| MD5 |
9d57166cf0d0c7cc46a1d073f0381611
|
|
| BLAKE2b-256 |
804f78d6e9e9d8372e33c0d79bb688c9de5f8e7bd6d2ea5ffd18596b4829bc77
|
File details
Details for the file specguard_ci-0.1.0-py3-none-any.whl.
File metadata
- Download URL: specguard_ci-0.1.0-py3-none-any.whl
- Upload date:
- Size: 21.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
90aa89b3df4fdd07ee3547fb50f0823f1543ac6a4be1f53b128c5c966f41cd92
|
|
| MD5 |
aaf1c3f0c22300be39eac744b661d72d
|
|
| BLAKE2b-256 |
96b57317e3b8ec03e83edb238a1affc9426a38b8bae7eeb65654086718d5e12b
|