Command line: rr

rr is installed by pip install rules-requirements, and available in Bazel as bazel run @rules_requirements//python:rr -- <command> ... (or bazel run @rules_requirements -- <command> ...). Under bazel run, relative paths resolve against the directory Bazel was invoked from.

Command

Purpose

Guide

validate

Check the model’s shape, references and coverage rules.

The model

scan (check-annotations)

Check that source annotations name defined ids; list them.

Source annotations

report (aggregate)

Join the model with evidence; write HTML/JSON/Markdown and the gap queue.

Reports

graph

Export the trace graph as DOT, Mermaid, SVG or JSON.

Reports

ingest

Print the test cases parsed from evidence files (debugging).

Evidence and ingestors

cases

List every test case key in the evidence, with status, declared ids and flags.

Evidence and ingestors

attribution

Print each case’s one owner (or none), how it got it and its declared ids; --check gates on quarantines, missing cases and lock drift; --suggest prints selectors.

Reports

sets lock / check / show

Write the verification-set lock from the evidence, check the lock against it, or print one entity’s set.

The model

check-report

Re-prove from a JSON report alone that no test case is owned by two entities.

Reports

migrate plan

Write the attribution worksheet: evidence that counts toward two or more entities.

Migrating to one requirement per test case

migrate apply

--stage tags: rewrite multi-id Python test tags to the owners decided on a worksheet; --stage model: write an explicit selector for every current owner into the model.

Migrating to one requirement per test case

migrate verify

Check test evidence from after migrate apply against the worksheet (and a baseline).

Migrating to one requirement per test case

wrap

Run a test binary and convert its output to traceability JUnit.

Test hooks

case

Append one test case to a JUnit file (shell and ad-hoc harnesses).

Test hooks

rr --version prints the installed version (rr 0.3.0).

Exit status: 0 on success; 1 when validation fails, scan finds undefined ids, or a report --fail-on / --pyramid-policy error condition holds, or an attribution issue is an error (with report --strict, any attribution warning); 2 when the model is invalid for scan, report, graph, attribution, sets and migrate, or a report format cannot be inferred (migrate still runs on a model whose only errors are shared-case / same-code-multiple-owners: resolving them is its job; sets on one whose only errors are about the lock); 3 when report finds a quarantined test case (after writing the reports; --on-attribution-error=warn keeps the status) and when sets lock refuses to lock one. attribution --check and sets check exit 1 on a quarantine, a missing case, lock drift or an error-level attribution issue; check-report exits 1 when the report breaks the one-owner partition, its counts disagree, or it names a JSON key twice in one object, 2 when it is no v2 report. migrate apply --stage model exits 1, writing nothing, when the owner table would change, the evidence holds a quarantine, a worksheet decision disagrees with the evidence, or the new claims fail check_claims. cases, attribution, sets and migrate plan exit 2 when the --evidence paths hold no evidence at all. migrate apply exits 1, and writes nothing unless given --partial, when it refused a file or could not find a decided case’s test in the module it names, and 2 when the worksheet is unreadable or an owner is not one of the ids its case counts toward. migrate verify exits 1 when a decided case does not declare exactly its owner or has no result, or, with --baseline, an undecided case’s ids changed or a case disappeared (--allow-missing excuses a missing case only when its target has no result file at all: a test.xml with no testcase means the target ran), and 2 when the worksheet is unreadable or invalid, the evidence or baseline holds none, or it names no build target while the worksheet’s cases belong to build targets. wrap exits with the wrapped command’s status; case exits 2 when it cannot record the case (no output file, more than one id, a malformed id or --artifact).

migrate apply judges the code only an if __name__ == "__main__": block runs like any other code unless given --trust-main-guard. That opt-in exclusion is best-effort: only use it together with a definitive check (the collection check, or migrate verify against fresh test evidence before merging).

The reference below is generated from the command’s own argument parser.

usage: rr [-h] [--version]
          {validate,scan,check-annotations,report,aggregate,graph,ingest,cases,migrate,attribution,sets,check-report,diff,serve,case,wrap}
          ...

Positional Arguments

command

Possible choices: validate, scan, check-annotations, report, aggregate, graph, ingest, cases, migrate, attribution, sets, check-report, diff, serve, case, wrap

Named Arguments

--version

show program’s version number and exit

Sub-commands

validate

validate the model

rr validate [-h] [--strict] [--format {text,json}] [--known-targets FILE]
            [--sets-lock PATH] [--junit PATH]
            [model ...]

Positional Arguments

model

Default: ['requirements']

Named Arguments

--strict

treat warnings as errors

Default: False

--format

Possible choices: text, json

Default: 'text'

--known-targets

labels of every test target, one per line (bazel query ‘tests(//…)’); a claim, config.variants entry or lock target naming any other label is an unknown-target error

Default: ''

--sets-lock

check this lock instead of config.sets_lock

Default: ''

--junit

also write one JUnit case per check family (rr.validate::shape, ::references, ::coverage-rules, ::claims, ::lock); default $XML_OUTPUT_FILE when set (under bazel test)

Default: ''

scan (check-annotations)

check source annotations

rr scan [-h] [--model MODEL [MODEL ...]] [--root ROOT] [--include INCLUDE]
        [--exclude EXCLUDE] [--files FILES [FILES ...]] [--list] [--json JSON]
        [--ignore-model-errors] [--strict]

Named Arguments

--model, --requirements

model files/directories

Default: ['requirements']

--root

source tree to scan (default: workspace)

Default: ''

--include

glob of files to scan (repeatable)

--exclude

glob of files to skip (repeatable)

--files

scan exactly these files (relative to –root)

--list

print every annotation found

Default: False

--json

write annotations as JSON

Default: ''

--ignore-model-errors

Default: False

--strict

treat annotation warnings (multi-verifies-annotation) as errors

Default: False

report

build the traceability report

rr report [-h] [--model MODEL [MODEL ...]] [--evidence [EVIDENCE ...]]
          [--format FORMAT] [--ingestor INGESTOR] [--html HTML] [--json JSON]
          [--md MD] [--out OUT] [--queue-out QUEUE_OUT] [--title TITLE]
          [--strict] [--fail-on {none,failed,unverified,gaps}]
          [--pyramid-policy {off,warn,error}] [--current-build KEY=VALUE]
          [--scan] [--sets-lock PATH | --no-lock] [--lane NAME]
          [--lane-targets FILE] [--on-attribution-error {fail,warn}]
          [--root ROOT] [--include INCLUDE] [--exclude EXCLUDE]
          [--files FILES [FILES ...]]

Named Arguments

--model, --requirements

model files/directories

Default: ['requirements']

--evidence, --junit

evidence files/dirs/globs

Default: ['bazel-testlogs']

--format

only use these ingestors (repeatable)

--ingestor

load an extra ingestor, module:attr

--html

Default: ''

--json

Default: ''

--md

Default: ''

--out

output file; format from extension (repeatable)

--queue-out

write the gaps as a JSON work queue

Default: ''

--title

Default: ''

--strict

treat model and attribution warnings as errors

Default: False

--fail-on

Possible choices: none, failed, unverified, gaps

Default: 'none'

--pyramid-policy

Possible choices: off, warn, error

Default: 'warn'

--current-build

current artifact identity; mismatching evidence is stale

--scan

also scan sources for implementation annotations

Default: False

--sets-lock

read this verification-set lock, not config.sets_lock

Default: ''

--no-lock

read no verification-set lock (the sets are not pinned)

Default: False

--lane

stamp the report with this lane (e.g. software)

Default: ''

--lane-targets

the targets this lane runs, one label per line: not-run members of other targets read ‘out of lane’ and their gaps stay out of –queue-out (verdicts never change)

Default: ''

--on-attribution-error

Possible choices: fail, warn

a quarantined test case (several ids, several claimants, one test code with several owners) makes rr report exit 3 after writing the reports (fail, the default); warn keeps the exit status (the cases still count for nobody, and every entity they name is INVALID)

Default: 'fail'

--root

source tree to scan (default: workspace)

Default: ''

--include

glob of files to scan (repeatable)

--exclude

glob of files to skip (repeatable)

--files

scan exactly these files (relative to –root)

aggregate

build the traceability report (alias)

rr aggregate [-h] [--model MODEL [MODEL ...]] [--evidence [EVIDENCE ...]]
             [--format FORMAT] [--ingestor INGESTOR] [--html HTML]
             [--json JSON] [--md MD] [--out OUT] [--queue-out QUEUE_OUT]
             [--title TITLE] [--strict]
             [--fail-on {none,failed,unverified,gaps}]
             [--pyramid-policy {off,warn,error}] [--current-build KEY=VALUE]
             [--scan] [--sets-lock PATH | --no-lock] [--lane NAME]
             [--lane-targets FILE] [--on-attribution-error {fail,warn}]
             [--root ROOT] [--include INCLUDE] [--exclude EXCLUDE]
             [--files FILES [FILES ...]]

Named Arguments

--model, --requirements

model files/directories

Default: ['requirements']

--evidence, --junit

evidence files/dirs/globs

Default: ['bazel-testlogs']

--format

only use these ingestors (repeatable)

--ingestor

load an extra ingestor, module:attr

--html

Default: ''

--json

Default: ''

--md

Default: ''

--out

output file; format from extension (repeatable)

--queue-out

write the gaps as a JSON work queue

Default: ''

--title

Default: ''

--strict

treat model and attribution warnings as errors

Default: False

--fail-on

Possible choices: none, failed, unverified, gaps

Default: 'none'

--pyramid-policy

Possible choices: off, warn, error

Default: 'warn'

--current-build

current artifact identity; mismatching evidence is stale

--scan

also scan sources for implementation annotations

Default: False

--sets-lock

read this verification-set lock, not config.sets_lock

Default: ''

--no-lock

read no verification-set lock (the sets are not pinned)

Default: False

--lane

stamp the report with this lane (e.g. software)

Default: ''

--lane-targets

the targets this lane runs, one label per line: not-run members of other targets read ‘out of lane’ and their gaps stay out of –queue-out (verdicts never change)

Default: ''

--on-attribution-error

Possible choices: fail, warn

a quarantined test case (several ids, several claimants, one test code with several owners) makes rr report exit 3 after writing the reports (fail, the default); warn keeps the exit status (the cases still count for nobody, and every entity they name is INVALID)

Default: 'fail'

--root

source tree to scan (default: workspace)

Default: ''

--include

glob of files to scan (repeatable)

--exclude

glob of files to skip (repeatable)

--files

scan exactly these files (relative to –root)

graph

export the trace graph

rr graph [-h] [--model MODEL [MODEL ...]] [--format {dot,mermaid,json,svg}]
         [--evidence [EVIDENCE ...]] [--methods] [--cases] [--out OUT]

Named Arguments

--model, --requirements

model files/directories

Default: ['requirements']

--format

Possible choices: dot, mermaid, json, svg

Default: 'mermaid'

--evidence

color nodes by status

Default: []

--methods

include test methods

Default: False

--cases

add a node per owned test case, with one edge from its one owner (needs –evidence)

Default: False

--out

Default: '-'

ingest

print the test cases parsed from evidence

rr ingest [-h] [--format FORMAT] [--ingestor INGESTOR] paths [paths ...]

Positional Arguments

paths

Named Arguments

--format
--ingestor

cases

list every test case key in the evidence (no model needed)

rr cases [-h] [--evidence [EVIDENCE ...]] [--target TARGET] [--json]
         [--format FORMAT] [--ingestor INGESTOR]

Named Arguments

--evidence

evidence files/dirs/globs

Default: ['bazel-testlogs']

--target

only this target (repeatable)

--json

print JSON instead of tab-separated lines

Default: False

--format

only use these ingestors (repeatable)

--ingestor

load an extra ingestor, module:attr

migrate

move to one requirement per test case: worksheet and codemod

rr migrate [-h] {plan,apply,verify} ...

Positional Arguments

migrate_command

Possible choices: plan, apply, verify

Sub-commands

plan

write the attribution worksheet: evidence counting toward 2+ entities

rr migrate plan [-h] [--model MODEL [MODEL ...]] [--evidence [EVIDENCE ...]]
                [--format FORMAT] [--ingestor INGESTOR] [--out OUT]
                [--json JSON] [--md MD] [--merge MERGE]
Named Arguments
--model, --requirements

model files/directories

Default: ['requirements']

--evidence

evidence files/dirs/globs

Default: ['bazel-testlogs']

--format

only use these ingestors (repeatable)

--ingestor

load an extra ingestor, module:attr

--out

worksheet to write (.rrplan, YAML); stdout if no output is given

Default: ''

--json

also write the worksheet as JSON

Default: ''

--md

also write the worksheet as Markdown

Default: ''

--merge

carry the decisions of an earlier worksheet over

Default: ''

apply

rewrite test tags to the owners decided on a worksheet

rr migrate apply [-h] --stage {tags,model} [--compress]
                 [--evidence [EVIDENCE ...]] [--ingestor INGESTOR]
                 [--root ROOT] [--only ONLY] [--unassigned {refuse,drop}]
                 [--line-length LINE_LENGTH] [--partial] [--dry-run]
                 [--model MODEL [MODEL ...]] [--no-collect-check]
                 [--trust-main-guard] [--python PYTHON]
                 [--pytest-args PYTEST_ARGS]
                 worksheet
Positional Arguments
worksheet

the decided .rrplan (or .json) worksheet

Named Arguments
--stage

Possible choices: tags, model

tags: split multi-id pytest/unittest tags; model: write an explicit selector for every current owner into the model (the owner table must stay unchanged under attribution: model)

--compress

–stage model: a ‘*’ glob where it selects exactly the entity’s cases (none skipped, no overlap)

Default: False

--evidence

–stage model: the evidence the owners come from (default: the worksheet’s inputs)

Default: []

--ingestor

load an extra ingestor, module:attr

--root

source tree to rewrite (default: workspace)

Default: ''

--only

only rewrite files below this path (repeatable)

--unassigned

Possible choices: refuse, drop

a multi-id test without a decision: leave its file unchanged (refuse) or drop its tags

Default: 'refuse'

--line-length

black’s line length, for lines that need wrapping

Default: 88

--partial

when files are refused or decided cases not found, still write each rewritten file that holds no such case and is not linked to a refused file or one holding such a case (default: write nothing). Two files are linked when one star-imports the other or passes it around as a module, or imports, references or subclasses a class, a test-named name or a name not defined at its top level; importing a plain helper function or constant does not link them. A file that cannot be read, a class whose base cannot be resolved, and an import call whose module cannot be named are linked to the files refused with them

Default: False

--dry-run

print a diff instead of writing; the collection check still runs (exit 1 when apply would refuse)

Default: False

--model, --requirements

check the decided owners exist

Default: []

--no-collect-check

skip the dynamic collection check (run pytest –collect-only before and after, and refuse unless every test keeps exactly the ids it should; it runs whenever anything would be written, with –partial and –dry-run too): write on the static guards alone, which are best-effort and cannot see tests pytest collects dynamically

Default: False

--trust-main-guard

leave out of the static guards the code only an ‘if __name__ == “__main__”:’ block runs (its body and the module-level functions only it reaches), which pytest does not run when it imports the module (default: judge that code like any other). The exclusion is best-effort: static analysis cannot prove what Python runs at import, so only use it together with a definitive check – the collection check, or rr migrate verify against fresh test evidence before merging

Default: False

--python

the interpreter whose pytest and project dependencies collect the tests for the collection check (default: the interpreter running rr)

Default: ''

--pytest-args

extra arguments for the collection check’s pytest, one shell-quoted string (e.g. “-c pytest.ini –rootdir . -p myplugin”, “–ignore=scripts”). Both collections run from –root with no path argument, so the project’s ini (testpaths included) picks what is collected; name paths here to collect others. With –strict-markers, pass ‘-p rules_requirements.hooks.pytest_plugin’ if the project loads rr’s plugin that way

Default: ''

verify

check test evidence from after apply against the worksheet (the check for Bazel projects)

rr migrate verify [-h] --worksheet WORKSHEET --evidence EVIDENCE
                  [EVIDENCE ...] [--baseline BASELINE [BASELINE ...]]
                  [--allow-missing] [--model MODEL [MODEL ...]]
                  [--format FORMAT] [--ingestor INGESTOR]
Named Arguments
--worksheet

the decided .rrplan (or .json) worksheet

--evidence

evidence from after the rewrite (files/dirs/globs, e.g. CI’s)

--baseline

evidence from before the rewrite: every undecided case must keep its ids, and no case may disappear

Default: []

--allow-missing

a case with no result is a warning, not an error, when its target has no result file at all in the evidence (a HITL or manual target CI does not run); a case missing from a target that ran stays an error. A target with any result file ran: a test.xml with no testcase (pytest collected nothing) or only Bazel’s synthetic whole-run result included

Default: False

--model, --requirements

check the decided owners exist, and the verified_by owner of decided cases that declare no id

Default: []

--format

only use these ingestors (repeatable)

--ingestor

load an extra ingestor, module:attr

attribution

print who owns each test case (and why), or check that nothing is wrong

rr attribution [-h] [--model MODEL [MODEL ...]] [--evidence [EVIDENCE ...]]
               [--ingestor INGESTOR] [--sets-lock PATH | --no-lock]
               [--target TARGET] [--unowned] [--format {tsv,json}] [--check]
               [--suggest]

Named Arguments

--model, --requirements

model files/directories

Default: ['requirements']

--evidence

evidence files/dirs/globs

Default: ['bazel-testlogs']

--ingestor

load an extra ingestor, module:attr

--sets-lock

read this verification-set lock, not config.sets_lock

Default: ''

--no-lock

read no verification-set lock (the sets are not pinned)

Default: False

--target

only this target (repeatable)

--unowned

only cases no entity owns (quarantined ones included)

Default: False

--format

Possible choices: tsv, json

Default: 'tsv'

--check

exit 1 on any quarantine, missing case, lock drift or error-level attribution issue

Default: False

--suggest

print the selector to add for each tag-owned or unclaimed-tag case

Default: False

sets

the verification-set lock: write it, check it, show one set

rr sets [-h] {lock,check,show} ...

Positional Arguments

sets_command

Possible choices: lock, check, show

Sub-commands

lock

compute the lock from the evidence; print the diff, or –write it

rr sets lock [-h] [--model MODEL [MODEL ...]] [--evidence [EVIDENCE ...]]
             [--format FORMAT] [--ingestor INGESTOR] [--sets-lock PATH]
             [--write] [--allow-removals] [--out PATH]
Named Arguments
--model, --requirements

model files/directories

Default: ['requirements']

--evidence

evidence files/dirs/globs

Default: ['bazel-testlogs']

--format

only use these ingestors (repeatable)

--ingestor

load an extra ingestor, module:attr

--sets-lock

the lock to read (default: config.sets_lock)

Default: ''

--write

write the lock (default: print the diff)

Default: False

--allow-removals

drop entries the evidence no longer has, including those of a suite:/record: pseudo-target it does not hold (default: keep them, so a crashed or filtered run never shrinks a set silently)

Default: False

--out

write here instead (relative: to the workspace root)

Default: ''

check

exit 1 when the lock disagrees with the evidence (missing cases, unlocked members, owner changes)

rr sets check [-h] [--model MODEL [MODEL ...]] [--evidence [EVIDENCE ...]]
              [--format FORMAT] [--ingestor INGESTOR] [--sets-lock PATH]
Named Arguments
--model, --requirements

model files/directories

Default: ['requirements']

--evidence

evidence files/dirs/globs

Default: ['bazel-testlogs']

--format

only use these ingestors (repeatable)

--ingestor

load an extra ingestor, module:attr

--sets-lock

the lock to read (default: config.sets_lock)

Default: ''

show

print one entity’s verification set

rr sets show [-h] [--model MODEL [MODEL ...]] [--evidence [EVIDENCE ...]]
             [--format FORMAT] [--ingestor INGESTOR] [--sets-lock PATH]
             entity
Positional Arguments
entity

a user need, requirement or mitigation id

Named Arguments
--model, --requirements

model files/directories

Default: ['requirements']

--evidence

evidence files/dirs/globs

Default: ['bazel-testlogs']

--format

only use these ingestors (repeatable)

--ingestor

load an extra ingestor, module:attr

--sets-lock

the lock to read (default: config.sets_lock)

Default: ''

check-report

re-prove from a JSON report alone that no test case is owned by two entities

rr check-report [-h] report

Positional Arguments

report

a JSON report (rules_requirements/report/v2)

diff

semantic diff of the model between two git refs

rr diff [-h] [--model MODEL [MODEL ...]] [--root ROOT] [--json] [--exit-code]
        old [new]

Positional Arguments

old

git ref (e.g. main, v1.0, HEAD~3)

new

git ref, or WORKTREE (default)

Default: 'WORKTREE'

Named Arguments

--model, --requirements

model files/directories

Default: ['requirements']

--root

repository root (default: workspace)

Default: ''

--json

Default: False

--exit-code

exit 1 when the model changed

Default: False

serve

interactive web editor with tracing, versioning and agents

rr serve [-h] [--model MODEL [MODEL ...]] [--evidence [EVIDENCE ...]]
         [--root ROOT] [--host HOST] [--port PORT] [--token TOKEN]
         [--allow-host ALLOW_HOST] [--author AUTHOR]
         [--current-build KEY=VALUE] [--lane-targets NAME=FILE] [--no-scan]
         [--no-llm] [--agent-model AGENT_MODEL] [--agent-effort AGENT_EFFORT]

Named Arguments

--model, --requirements

model files/directories

Default: ['requirements']

--evidence

test evidence (e.g. bazel-testlogs)

Default: []

--root

repository root (default: workspace)

Default: ''

--host

Default: '127.0.0.1'

--port

Default: 8080

--token

require this bearer token on API requests

Default: ''

--allow-host

extra Host header value to accept (e.g. behind a proxy)

--author

default author for edits and commits, “Name <email>”

Default: ''

--current-build
--lane-targets

the targets lane NAME runs, one label per line: the case ledger can filter by lane

--no-scan

skip the source annotation scan

Default: False

--no-llm

disable LLM-backed agent workflows

Default: False

--agent-model

Claude model for agents (default claude-opus-5-5)

Default: ''

--agent-effort

effort for agent requests (default high)

Default: ''

case

Append one test case to the JUnit file at –out (default $XML_OUTPUT_FILE), creating it if needed. Exits 0 whatever the case’s status.

rr case [-h] [--out OUT] --name NAME [--status {passed,failed,error,skipped}]
        [--classname CLASSNAME] [--suite SUITE] [--requirement ID]
        [--level LEVEL] [--artifact KEY=VALUE] [--message MESSAGE]
        [--duration DURATION] [--file FILE]

Named Arguments

--out

JUnit file to append to (default: $XML_OUTPUT_FILE)

Default: ''

--name

the case’s name

--status

Possible choices: passed, failed, error, skipped

Default: 'passed'

--classname

the case’s classname (default: the suite)

Default: ''

--suite

suite to append to (default: the name part of $TEST_TARGET, else rr)

Default: ''

--requirement

the ONE requirement id the case verifies

--level

the verification level the case provides

Default: ''

--artifact

artifact identity (repeatable)

--message

failure, error or skip message

Default: ''

--duration

seconds

Default: 0.0

--file

source file of the test code, recorded as rr.file

Default: ''

wrap

run a test binary and emit traceability JUnit

rr wrap ...

Positional Arguments

rest