Install
Sembl is a Python command-line tool. You need Python 3.10 or newer. The stable package is live on PyPI:
pip install semblThat gives you the sembl command. Confirm it is there:
sembl --versionIf you prefer an isolated CLI install, use uv:
uv tool install semblpip install "sembl[graph-pipeline]". pip install sembl always gives you the latest stable release (0.2.0).Verify a change
After your agent has edited the repo, point sembl verify at a bounds file and (optionally) the agent's own report:
sembl verify --wo-file bounds.json --report agent.jsonSembl reads the real git diff in the working tree, compares it to the bounds, cross-checks the report, and prints a single verdict with reasons:
sembl verify - BLOCK
files changed ....... 7
out of scope ........ infra/deploy.yaml
forbidden hits ...... none
fabricated claims ... src/payments/refund.ts
validation evidenced missing: pytest
churn vs budget ..... 7 files > 6The three verdicts:
- PASS - the change stayed in bounds and the report checks out. Exit code 0.
- WARN - soft signals (out-of-scope edit within tolerance, churn over budget, a test claimed passed with no evidence). Exit code 0, or 1 with
--strict. - BLOCK - a hard breach: a forbidden-area edit or a fabricated file claim, or - in strict mode only, and only once
scope_toleranceis exceeded - an out-of-scope edit. Exit code 1.
--json for machine-readable output and --strict to make WARN a failing exit code. Every check is deterministic - no model runs, so the verdict is identical on every machine.The bounds contract
A bounds file is plain JSON. verify reads only five fields, so anything that can emit them can drive the gate:
{
"editable_paths": ["src/auth/", "src/api/routes/login.ts"],
"forbidden_areas": ["migrations/", "infra/"],
"churn_budget": { "max_files": 6, "max_lines": 200 },
"scope_tolerance": { "max_fraction": 0.25 }
}| Field | Meaning |
|---|---|
editable_paths | Path prefixes the change may touch. A changed file outside all of them is out-of-scope. |
forbidden_areas | Path prefixes the change must not touch. A hit here is a BLOCK. |
churn_budget.max_files | Soft cap on changed files (WARN if exceeded). Optional. |
churn_budget.max_lines | Soft cap on added+deleted lines (WARN if exceeded). Optional. |
scope_tolerance | How many out-of-scope edits are absorbed before they count toward the verdict (default: up to 25% of changed files) - real changes routinely touch a file or two no bound named. Set to {} for zero tolerance. Optional. |
Paths are prefix-matched: src/auth/ matches src/auth/login.ts. Tests are always allowed alongside in-scope edits. Produce the file by hand, from Spec Kit (below), or with the generation beta - any extra keys are ignored, so a full work-order.json works unchanged.
The executor report
If you pass --report report.json, Sembl cross-checks the agent's own claims against the diff. It never trusts the report - it only catches contradictions:
- Fabricated claims (BLOCK) - a file the report says it changed that the diff does not show. Recognized:
files_modified,files_changed,files,changes: [{"file": "..."}]. - Unevidenced validation (WARN) - a
tests_passed: true/status: "passed"/checks: [...]claim with no backingexit_code: 0or captured output.
The report is whatever JSON your agent or harness emits; the report is optional, and scope/forbidden/churn checks run with or without it.
Gate in CI
Run verify --strict as a step on every agent-authored PR. A forbidden edit or a fabricated claim fails the build before a human reads a line:
# GitHub Actions step
- name: sembl verify
run: |
pip install sembl
sembl verify --wo-file bounds.json --report agent.json --strictBecause the check is deterministic, the verdict is reproducible and auditable - the same diff and bounds always yield the same result.
Use it from an agent: MCP & Skills
Agents can call the gate directly - no shell, no clone, no API key. The fastest path is zero-install: paste this into your MCP client's config (Claude Code .mcp.json, Cursor, Windsurf, …) and uvx fetches and runs it on demand:
{
"mcpServers": {
"sembl": {
"command": "uvx",
"args": ["--from", "sembl[mcp]", "sembl-mcp"]
}
}
}Or install it and run the server yourself over stdio:
pip install "sembl[mcp]"
sembl-mcpEither way it mirrors the whole CLI:
verify_change- the gate. Pass a unifieddiff(no checkout needed) pluseditable_paths/forbidden_areas(or abounds_file) and an optionalreportof what the actor claims it did. Returns the verdict and per-check findings.bounds_from_spec- derive bounds from a Spec Kittasks.md, a preset, or a custom config.list_presets,doctor- the declarative presets, and deterministic repo diagnostics.clarify_task,generate_work_orderbeta - the generation half, for completeness.
Main agent verifies sub-agent
The general case for delegating safely: the sub-agent declares the files it may touch and reports what it did; the orchestrator gates the result instead of trusting the self-report.
// verify_change arguments
{
"diff": "<the sub-agent's patch>",
"editable_paths": ["src/auth/"],
"forbidden_areas": ["migrations/"],
"report": { "changed_files": ["src/auth/login.ts"], "tests_passed": true }
}This catches a sub-agent that edits outside its declared files, touches a forbidden area, claims a file it never changed, or says "tests passed" with no evidence - deterministically, with no second model in the loop.
Drop-in Agent Skills
Copy the skills in skills/ into .claude/skills/:
sembl-verify-subagent- verify a sub-agent's work before accepting it.sembl-setup-bounds- create a bounds file for a repo.sembl-gate-pr- gate a PR or local diff before merge.
Bounds from GitHub Spec Kit
Spec Kit plans what to build and writes specs/<feature>/tasks.md, where each task already names the exact file paths it will touch. sembl bounds turns those into a bounds file:
sembl bounds --spec-kit specs/001-login --out bounds.json
# or point straight at the file
sembl bounds --spec-kit specs/001-login/tasks.md --out bounds.jsonIt collects the task file paths into editable_paths and derives a grounded max_files budget. forbidden_areas is left empty for you to fill with whatever the spec declared off-limits (migrations, infra, generated code). Then verify as usual. Tessl, Kiro, or a hand-written file work the same way.
Draft a Work Order beta
Open a terminal inside a repo and describe the task in plain words:
sembl generate --task "fix the login redirect bug" --provider openaiSembl reads the repo, asks a model to write a Work Order, grounds every path against your real code, and writes a folder under .sembl/:
.sembl/work-orders/wo-<project>-<timestamp>-<slug>/
work-order.md # human-readable
executor-prompt.md # paste into your agent
validation-plan.md # run after the agent finishes
work-order.json # this is what verify --wo-file readsBrowse them with sembl list, sembl show, and sembl show --file executor-prompt. The work-order.json doubles as a bounds file for verify.
Set an API key beta
Generation asks a language model to write the Work Order, so it needs one provider API key. (The gate itself needs none.) Set the key as an environment variable; Sembl reads it automatically.
| Provider | Environment variable | --provider | Default model |
|---|---|---|---|
| OpenAI | OPENAI_API_KEY | openai | gpt-4o |
| Anthropic | ANTHROPIC_API_KEY | anthropic | claude-sonnet-4-6 |
| Google Gemini | GEMINI_API_KEY | gemini | gemini-2.5-flash |
| NVIDIA NIM | NVIDIA_API_KEY | nvidia | mistralai/mistral-medium-3.5-128b |
| OpenRouter | OPENROUTER_API_KEY | openrouter | moonshotai/kimi-k2 |
| TokenRouter | TOKENROUTER_API_KEY | tokenrouter | MiniMax-M3 |
| Ollama (local) | none - OLLAMA_HOST optional | ollama | qwen2.5-coder:7b |
| Claude Code CLI | none - uses your claude login | claude-cli | whatever your CLI login defaults to |
Windows - PowerShell
# just this terminal
$env:OPENAI_API_KEY = "sk-your-key"
# or save it for every future terminal
setx OPENAI_API_KEY "sk-your-key"setx, open a new terminal - it doesn't change the one you're in.macOS / Linux - bash or zsh
# just this terminal
export OPENAI_API_KEY="sk-your-key"
# or save it permanently
echo 'export OPENAI_API_KEY="sk-your-key"' >> ~/.zshrcProviders beta
All providers work the same way - change --provider and the matching key. A few notes:
- OpenAI and Anthropic are the most reliable at producing clean, structured Work Orders.
- Gemini is fast and inexpensive for everyday tasks.
- NVIDIA NIM, OpenRouter, and TokenRouter reach open and catalog models; pick a strong instruction-follower for JSON discipline. Pass the id with
--model, e.g.moonshotai/kimi-k2. - Ollama runs a model locally - no key, no rate limits, offline. Install Ollama,
ollama pull qwen2.5-coder:7b, then--provider ollama. - Claude Code CLI drives your existing
claudelogin instead of a raw API key - no key to manage, uses whatever subscription/auth the CLI already has.
sembl generate --task "..." --provider anthropic --model claude-sonnet-4-6Graph pipeline beta
Generation can use graph context - a structural map of your repo built by Graphify and code-review-graph - to ground the scope it drafts. It is best-effort and off the critical path.
pip install "sembl[graph-pipeline]"
# build the graphs once
graphify update C:\path\to\repo --no-cluster
code-review-graph build --repo C:\path\to\repo --data-dir C:\path\to\repo-crg-data --skip-flows
# then generate against them
$env:CRG_DATA_DIR = "C:\path\to\repo-crg-data"
sembl generate --repo C:\path\to\repo --task "..." --graph-mode requiredRun sembl doctor to see what's installed and built and the exact command to fix each gap (--fix installs missing tools; --json for tooling). Control graph use with --graph-mode auto|required|off and rebuild with --refresh-graph.
How verify works
The gate is deliberately small and deterministic. Given a bounds file and (optionally) a report, it:
- Reads the diff - modified, staged, and untracked files vs HEAD, normalized so an editor's line-ending rewrites don't count as edits.
- Classifies each file - in
editable_paths, inforbidden_areas, or out-of-scope. Tests count as in-scope. - Cross-checks the report - claimed files not in the diff are fabrications; "passed" claims with no evidence are flagged.
- Measures churn - file and line counts against the budget.
- Rolls up a verdict - BLOCK for a hard breach, WARN for a soft signal, PASS when clean.
No language model runs at any step, and Sembl makes no judgement about whether the code is good - only whether the change is objectively inside the lines it was given. That is the whole claim, and it's why the verdict is reproducible and free.
Troubleshooting
verify says everything is out-of-scope
Your editable_paths are empty or too narrow. Check the bounds file. If you built it with sembl bounds, confirm the Spec Kit tasks.md actually names file paths - prose tasks produce no paths.
verify exits 1 but I only see warnings
You passed --strict, which turns WARN into a failing exit code for CI. Drop --strict to let warnings pass.
My fabrication / validation checks do nothing
Those only run when you pass --report. Sembl recognizes common report shapes (files_modified, changes[], tests_passed, a checks[] list) - match one of them.
A formatter dirtied every file and verify flags them
Sembl ignores line-ending-only rewrites already. If real reformatting changed many files, that's a genuine churn signal - tighten the agent's task or raise max_files deliberately.
Generation: "No API key found" / quota / model errors
These come from the optional generation beta, not the gate. Set the matching provider key (on Windows, open a new terminal after setx), check billing/quota, or pass a model you have access to with --model.
"Graph context required but unavailable"
You used --graph-mode required but no graph context was available. Run sembl doctor to see what's missing, build the graphs, or switch to --graph-mode auto.