Skip to content
SLCR

Concepts

Validators

How acceptance criteria, validators, and tools turn requirements into checks that pass or fail, and the difference between vetting and checking.

A requirement is a statement about how the software should behave. Validators are how SLCR tests that statement. Each validator runs a tool, such as a test runner, a linter, or a script, and reports green (pass) or red (fail). slcr check runs every validator and combines the results through the spec's links into a status for each requirement. This is how SLCR detects drift: a failing validator points to the specific requirement the code no longer meets.

The path from a requirement to a result has five parts:

  1. A requirement states what must hold.
  2. Its acceptance criteria describe what "done" looks like.
  3. Validators each name a requirement, optionally one of its criteria, and the tool that checks it.
  4. A tool descriptor describes how to run that tool.
  5. slcr check runs the tools and reports which requirements are satisfied.

Acceptance criteria⁠Link to this section

An acceptance criterion describes, for one requirement, something that could be checked to show the requirement holds:

YAML
acceptanceCriteria:
  - kind: acceptanceCriterion
    id: AC-001
    title: Table schema is declared
    body: >-
      A table named `todo_list_item` is declared with a string column `title`
      and a boolean column `done`. A structural match against the schema or
      migration source is sufficient.

Criteria are usually written before the code exists, so they describe behavior rather than naming files, functions, or code patterns. How the behavior is checked is left to validators. A criterion asserts nothing on its own: the requirement is the claim, and validators provide the evidence. Criteria are optional, but they make it clearer what a validator should check.

Validators⁠Link to this section

A validator is an entry in a validator file, which sits in the directory of the code it checks:

TOML
# src/views/items/.validators.slcr.toml
formatVersion = "1"

[[validator]]
requirement = "todo-api/REQ-002"
criterion = "AC-004"
tool = "ast-grep"
description = "POST handler is defined and registered with the router"
with = { rule = "rules/post-handler.yml" }

[[validator]]
requirement = "todo-api/REQ-002"
tool = "cargo-nextest"
with = { test = "items::post::creates_item" }

Each validator names the requirement it checks (as a qualified ID), optionally the criterion it tests, the tool that runs the check, and the tool's settings under with. A directory holds at most one validator file. Because the file sits beside the code it checks, it moves when that code moves.

The dependency runs one way⁠Link to this section

Validators name requirements, and requirements never name validators. The spec states what must be true and stays readable for people who never look at the code. Implementation detail, such as test names, file paths, and tool settings, lives with the code. Tests can be rewritten without changing the spec, and the spec can be edited without knowing how it's tested.

Several validators per criterion⁠Link to this section

Turning an acceptance criterion into validators is called materializing it. One criterion may need several validators. For example, the same authentication check might need to run against handlers in two directories. A requirement's own validators combine with AND: every one must pass for the requirement to be satisfied.

A validator only provides evidence if it can fail. This is why the usual workflow is red-green: write the validator, see it fail before the code exists, then implement until it passes.

Tools and descriptors⁠Link to this section

SLCR has no built-in tools. Each project declares the programs its validators may run in SLCR.toml, and a tool descriptor describes how to run each one. A descriptor is a small, declarative TOML file. It is never executed itself. It specifies:

  • a schema for the validator's with settings, so a mistake in a validator is caught before anything runs
  • the command to run, with placeholders filled from the validator
  • the working directory, and optionally an environment and timeout
  • which exit codes count as a pass
TOML
formatVersion = "1"
name = "cargo-nextest"
version = "1.0.0"

[validator.schema]
type = "object"
required = ["test"]
additionalProperties = false
properties.test = { type = "string", minLength = 1 }

[invoke]
argv = ["cargo", "nextest", "run", "--no-fail-fast", "--no-tests=fail",
        "-E", "test(={with.test})"]
cwd = { nearest = "Cargo.toml" }

[results]
pass = [0]

A descriptor can wrap any program, including a short script committed to the repository, so validators can check anything a program can check: tests, code structure, configuration, or generated files. In the current version, a validator's result comes only from the tool's exit code.

Pins⁠Link to this section

When a tool is declared, SLCR.toml records where its descriptor came from and a pin: a cryptographic hash (BLAKE3) of the descriptor's exact contents. slcr refuses to use a descriptor whose contents don't match its pin. This means the commands that run on any machine are exactly the ones recorded, and reviewed, in the project's history.

Vet and check⁠Link to this section

vet and check answer two different questions:

  • slcr vet asks whether the project is well formed: specs, validator files, the manifest, and installed descriptors. It finds broken links, cycles, duplicate IDs, and validators whose settings don't match their tool's schema. It runs nothing.
  • slcr check asks whether the validators pass. It vets the project first and runs nothing if vetting fails. It then runs every validator and reports each requirement's status. In the current version, every run of check reruns every validator.

Each validator produces one of three results:

ResultMeaning
GreenThe tool ran and passed.
RedThe tool ran and failed. Either the code or the validator needs to change.
Could not be runThe tool couldn't start, for example because it isn't installed. This proves nothing either way, so check fails.

Running slcr check runs the commands a project's descriptors declare, the same way running a test suite runs its tests. It carries the same trust considerations as running any project's tests.