Skip to content
Conduit

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

RachidChabane/shallnotcommunitylistedunreviewed

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

Skills do not use an MCP config. Follow the steps in the README below.

README

synced Oct 21, 2026

An agent reports a feature done with all tests passing. shallnot blocks the end of its turn because requirement PWD-2~1 has no bound test. The agent fixes its tags and the gate passes.

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, a CLAUDE.md that 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 codeMeaning
0clean
1blocked: some requirement lacks a passing test, or a tag is wrong
2no 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

Contributing

Bug reports and pull requests are welcome. CONTRIBUTING.md has the build and test commands. Security issues: SECURITY.md.

License

Apache-2.0

↑↓ navigate openesc close