skill
shallnot
Traceability gate for coding agents: blocks "done" until every requirement has a passing test. Hooks into Claude Code, Codex, Copilot, Cursor, Gemini CLI and more; works with pytest, Jest, Vitest, JUn
Set up
Skills are installed by placing their folder where your agent loads skills.
The download comes from github.com/RachidChabane/shallnot itself, not from our server, so what you get is exactly what its author published.
Install instructions come from the README
README
synced Oct 21, 2026
Your specs give each requirement an ID. Tests say which requirement they verify. shallnot reads the specs, the test sources and the JUnit XML, and fails unless every requirement is cited by a test that actually ran and passed. Hooked into the agent, it sends the findings back and the agent keeps working until the gate is green.
It is one static binary. It never touches the network or a model, so the same inputs always give the same report.
Use it with your agent
Install the binary:
go install github.com/RachidChabane/shallnot/cmd/shallnot@latest
or take a prebuilt one from the
releases page
(Linux, macOS, Windows; amd64 and arm64; checksums.txt alongside).
Then, in your project:
shallnot init
This writes:
shallnot.yaml, filled in for the test runner it finds (pytest, Jest, Vitest, Maven, Gradle, Go)- a section in
AGENTS.md, aCLAUDE.mdthat imports it, and project skills (.agents/skills,.claude/skills): how to write a requirement, tag a test, run the gate, review a tagged test - an end-of-turn hook for Claude Code, and for each other harness the project is set up for: Codex, GitHub Copilot, Cursor, Gemini CLI, OpenCode, Qwen Code, Factory Droid, Goose
Commit those files. From then on you ask for features the usual way and never mention shallnot. The agent writes the requirement, the code and the tagged tests. If it tries to finish on a blocked gate, the hook hands it the findings. After three blocks in a row the hook lets go and tells you.
VS Code Copilot picks up the Claude Code hook. A harness that cannot hold the end of a turn relies on the instructions and skills it reads, and CI catches what it lets through. The table for every harness is in docs/agents.md.
There is also a Claude Code plugin, for using the skills in every project without committing anything:
/plugin marketplace add RachidChabane/shallnot
/plugin install shallnot@shallnot
What it looks like
A requirement in a Markdown (or YAML) spec, as ID~REVISION:
- **PWD-2~1**: WHEN a password contains the account's username THE SYSTEM
SHALL reject it.
A test bound to it. The tag goes wherever the runner will report it:
@pytest.mark.verifies("PWD-2~1") # pytest
it("rejects the username [verifies PWD-2~1]", ...) // Jest, Vitest
@DisplayName("rejects the username [verifies PWD-2~1]") // JUnit 5
t.Run("rejects the username [verifies PWD-2~1]", ...) // Go
The gate:
$ shallnot check --specs spec.md --tests tests --results junit.xml
shallnot: FAIL
focus: every known requirement
requirements: 3 known, 3 in focus (1 covered, 0 failed, 0 skipped, 0 not run, 1 uncovered, 1 non-testable)
tests: 2 in results, 2 bound, 0 untagged
findings: 1 error, 0 warning, 0 info (1 blocking)
REQUIREMENTS
PWD-1~1 covered spec.md:3
passed tests.test_password › test_long_passwords_are_accepted tests/test_password.py:11
passed tests.test_password › test_short_passwords_are_rejected tests/test_password.py:6
PWD-2~1 uncovered spec.md:5
PWD-3~1 non_testable spec.md:7
FINDINGS
error uncovered_requirement spec.md:5 requirement PWD-2~1 has no bound test
A tag only counts if it shows up in the results file, so a tag in a comment or on a test that never ran binds nothing. When the meaning of a requirement changes, bump its revision: every test citing the old one fails the gate until someone re-checks it.
shallnot gate runs your test commands and then checks. shallnot check
only reads results that already exist.
| Exit code | Meaning |
|---|---|
0 | clean |
1 | blocked: some requirement lacks a passing test, or a tag is wrong |
2 | no verdict (missing results, bad config). Says nothing about coverage. |
Try it on a tiny project: examples/quickstart.
In CI
- run: pytest --junitxml=junit.xml
continue-on-error: true
- uses: RachidChabane/shallnot@v0.4.2
with:
args: --specs specs --tests tests --results junit.xml
The action writes a job summary and annotates the offending lines. shallnot-demo is a small repository gated this way.
What a pass does not prove
A pass means every requirement is cited by a test that ran and passed. It
does not mean the test is any good. A tagged test that asserts nothing still
passes the gate. Judging that is a review job, and the shallnot-review
skill tells the agent how to do it.
Documentation
- docs/agents.md:
init, the hooks, the plugin - docs/spec-format.md: requirements in Markdown and YAML
- docs/binding.md: tagging tests, per runner
- docs/configuration.md:
shallnot.yaml, flags, severities - docs/report.md: the JSON report, every field and finding category
- docs/pipeline-gate.md: gating an unattended agent pipeline, several repositories
- docs/comparison.md: OpenFastTrace, Doorstop, StrictDoc, sphinx-needs
- schemas/: JSON Schemas for the report, the config and YAML specs
- llms.txt: the same index, for agents
Contributing
Bug reports and pull requests are welcome. CONTRIBUTING.md has the build and test commands. Security issues: SECURITY.md.