Configuration

August 30, 2026 · View on GitHub

This is the reference for every setting ripr reads today, including the repository ripr.toml file. It pairs with:

What can be configured today

ripr currently reads configuration from six surfaces:

  1. CLI flags on the ripr binary.
  2. Repo config in the nearest ripr.toml at or above the selected root.
  3. LSP initializationOptions sent by an LSP client (e.g. the VS Code extension) on initialize.
  4. VS Code extension settings under the ripr.* namespace, which the extension translates into server arguments and LSP options.
  5. Environment variables for narrow runtime tuning of expensive local paths.
  6. Repo policy files under .ripr/, including static-language allowlists, test intent, and suppressions.

ripr.toml is repository-scoped. Starting at the selected root, ripr walks parent directories until it finds the nearest config, then stops at a Cargo workspace manifest or .git boundary. A package-only Cargo.toml does not stop the walk. ripr does not read global user config or hidden alternate config files. Environment variables are process-local tuning knobs and are documented separately from repo policy.

Configuration is for policy and tuning, not a prerequisite for first value. ripr.toml is optional, and missing config is the normal first-run state — it uses built-in defaults. ripr init is only for teams that want to commit those defaults to disk so they can review, version, and tune repo policy; it does not unlock basic usefulness or enable a stronger mode. The defaults-first adoption contract in RIPR-SPEC-0009 defines the missing-config and generated-config behavior that must remain advisory.

Defaults-first operator profile

The built-in missing-config profile and the ripr init generated profile are intended to be policy-equivalent. ripr init writes that policy into ripr.toml so a team can review, version, and tune it; it does not enable a stronger or more useful mode than the zero-config default. Most users do not need to run it.

SurfaceDefault policy
AnalysisNormal/default scans use draft mode and include unchanged tests as static evidence.
OraclesSnapshot and mock-expectation oracles are medium; broad error checks are weak.
Seam diagnosticsSaved-workspace LSP seam diagnostics are on, with explicit config or initialization options allowed to disable them.
Report capsContext packets and collect-context commands include up to 5 related tests by default.
SuppressionsBadge renderers look for .ripr/suppressions.toml; a missing file is normal.
BadgesRepo badges count configured-visible unresolved seam gaps and stay advisory unless an explicit failure policy is selected.
CacheFull repo seam cache stores up to 20000 seams by default, compact repo seam cache stores up to 100000 seams by default, and large repos can opt into higher process-local limits.
CIGenerated GitHub workflows upload advisory pilot/report/agent artifacts, keep SARIF rendering/upload optional, and use continue-on-error by default.
CalibrationRuntime data is imported only when explicitly supplied; ripr does not run mutation testing by default.

Operator mode vocabulary maps to concrete analysis modes:

Operator stanceConcrete modeScope
Fastest feedbackinstantChanged Rust files only.
Normal/defaultdraftRust files in packages touched by the diff, including unchanged tests.
PR fast scanfastSame package-local scope as draft for now.
Deep static scandeepAll Rust files in the workspace.
Ready preflightreadyAll Rust files in the workspace before separate mutation confirmation.

Repo-scoped public signals intentionally filter out repository automation and non-production trees so badges and repo seam reports describe the package surface, not the toolchain around it. The production filter excludes paths under xtask/, top-level fixture data, editor extension sources, target/, node_modules/, test/example/bench trees, and src/tests.rs. Passing a fixture workspace itself as --root still analyzes that fixture normally.

CLI flags

The CLI is the canonical, fully-supported configuration surface. All defaults below come from crates/ripr/src/cli/help.rs and crates/ripr/src/app.rs.

Top-level

FlagEffect
--help, -hPrint top-level help.
--version, -VPrint the ripr version.

ripr init

Optional. Writes a repo-local ripr.toml at the selected workspace root that materializes the built-in defaults as repo policy so a team can review, commit, and tune them. ripr.toml is not required — missing config uses the same defaults. With --ci github, ripr init also writes a non-blocking GitHub Actions workflow for pilot/report/agent artifacts, optional repo-local cockpit rendering, and optional SARIF rendering/upload. It does not run mutation testing, enable CI blocking policy, or unlock basic CLI usefulness.

ripr init [--root PATH] [--ci github] [--dry-run] [--force]
FlagDefaultNotes
--root PATHcurrent directoryWorkspace root where ripr.toml should be written.
--ci github(off)Also write .github/workflows/ripr.yml. The workflow installs ripr, runs ripr pilot, uploads pilot/report/agent artifacts, writes repo badge JSON, optionally renders and uploads SARIF when RIPR_UPLOAD_SARIF is true, and uses continue-on-error so the default path is advisory.
--dry-run(off)Preview the run without writing. Prints a plan naming each target path and what would happen to it (create, overwrite, leave existing), then the body of each file that would be written.
--force(off)Overwrite an existing ripr.toml or generated workflow. Without this flag, existing repo policy and workflow files are left unchanged.

--dry-run resolves the same preconditions as the real run, so the preview cannot disagree with the run it previews. Whenever the real run would fail, the dry run fails with the same message and the same exit status instead of printing a config it could not have written. The blockers are:

BlockerApplies when
--root is not a directoryalways
ripr.toml already exists, no --forceonly without --ci — see below
the target workflow already exists, no --forcewith --ci
a target's parent exists but is not a directory (for example .github is a file)always

An existing ripr.toml is only a blocker when there is nothing else to do. With --ci, the run still has a workflow to write, so an existing config is reported as leave existing and the run proceeds — that is the case shown below. Because every target is checked before anything is written, a run that cannot finish writes nothing at all rather than half-initializing the repository.

$ ripr init --ci github --dry-run
ripr init plan (dry run — nothing was written)
  leave existing ./ripr.toml
  create         ./.github/workflows/ripr.yml

# ./.github/workflows/ripr.yml
name: RIPR
...

Rerun without --dry-run to apply.

A target listed as leave existing prints no body, because nothing about that file would change.

ripr pilot

Runs the zero-config first-run repo evidence path and writes a pilot packet. Missing ripr.toml is the normal first-run state; the command uses built-in defaults unless repo policy or explicit flags override them.

ripr pilot [--root PATH] [--out PATH] [--mode MODE] [--max-seams N]
FlagDefaultNotes
--root PATHcurrent directoryWorkspace root to analyze.
--out PATHtarget/ripr/pilotDirectory for repo-exposure.{json,md}, agent-seam-packets.json, and pilot-summary.{json,md}.
--mode MODEripr.toml analysis.mode, otherwise draftOne of instant, draft, fast, deep, ready.
--max-seams N5Maximum ranked seams shown in the pilot summary. Must be positive.

ripr check

Runs the static exposure analysis and renders findings.

FlagDefaultNotes
--root PATHcurrent directoryWorkspace root used for diff and source discovery.
--base REVorigin/mainGit revision used as the diff base when --diff is not given.
--diff PATH(unset)Path to a unified diff file. Overrides --base.
--mode MODEripr.toml analysis.mode, otherwise draftOne of instant, draft, fast, deep, ready. See the mode reference.
--format FORMAThumanOne of human (alias text), json, github.
--json(off)Shortcut for --format json.
--no-unchanged-testsripr.toml analysis.include_unchanged_tests, otherwise tests includedLimits the source index to changed Rust files. By default unchanged tests are part of the index so Reach evidence can find them.

Environment Variables

Environment variables are process-local tuning knobs. They do not change repo-policy defaults in ripr.toml, and they should be set only for the command that needs the tuning.

VariableDefaultEffect
RIPR_CACHE_DIR(none)Relocate the entire cache base directory. When set to a non-empty path, all cache reads and writes use that path instead of {workspace_root}/target/ripr/cache. Useful for read-only or immutable source checkouts and for redirecting the cache to a faster or larger volume. When unset or empty, default behaviour is unchanged. ripr doctor reports whether RIPR_CACHE_DIR is active and shows the resolved location and total size.
RIPR_MAX_DIFF_CHANGED_RUST_LINES2000Maximum added plus removed Rust diff lines that ripr check will expand into probes before failing closed with diff_scope_oversized. With --json, the command still exits non-zero but emits a limited artifact with analysis_scope.run_status = "diff_scope_oversized" and downstream_consumable = false. This protects constrained runners from large code-motion diffs that touch few files but produce thousands of probes. Raise only for a single command on a machine with enough memory, or split the extraction PR. Must be a positive integer. Invalid values fail with a diagnostic naming the variable.
RIPR_MAX_DIFF_INDEX_FILES800Maximum Rust files that diff-scoped analysis will load into the Rust index before failing closed with diff_scope_oversized. With --json, the command still exits non-zero but emits a limited artifact with analysis_scope.run_status = "diff_scope_oversized" and downstream_consumable = false. This protects runners from package-wide index expansion on large multi-crate diffs. Raise only for a single command on a machine with enough memory, or reduce the diff scope. Must be a positive integer. Invalid values fail with a diagnostic naming the variable.
RIPR_PARTIAL_DIFF_FILE_BUDGET200Changed-line files a diff-scoped analysis inspects before returning a bounded limited_partial_scope result (RIPR-PROP-0019): the run analyzes a deterministic whole-file partition (supported-language files first, then package path, then file path) and discloses the exact selected paths, lower-bound uninspected counts, a named stop reason, and gate_eligibility = "ineligible" under analysis_scope in the JSON output. A partial result is advisory only — never a gate, baseline, badge, or RIPR Zero input — and raising this variable is the only continuation route. Values above the effective RIPR_MAX_DIFF_INDEX_FILES limit (its env override when set, otherwise the 800 default) are clamped to that effective limit with a disclosure. Must be a positive integer; invalid values fail closed as partial_budget_invalid.
RIPR_PARTIAL_DIFF_LINE_BUDGET1000Added plus removed changed lines a diff-scoped analysis inspects before returning limited_partial_scope. A later whole file that would exceed the remaining budget is excluded (never an overshoot); the first selected file is always analyzed even when it alone exceeds the budget (stop reason line_budget_exceeded_on_first_file). Values above the effective RIPR_MAX_DIFF_CHANGED_RUST_LINES limit (its env override when set, otherwise the 2000 default) are clamped to that effective limit with a disclosure — so a runner that raises the max limit accepts a line budget up to the same ceiling instead of silently truncating it back to the default. Must be a positive integer; invalid values fail closed as partial_budget_invalid.
RIPR_REPO_SEAM_CACHE_LIMIT20000Maximum classified seam count per shard in the full repo seam cache for a completed repo-exposure run. Larger cache entries are written as bounded shard files under the cache base directory. Raise this to reduce shard count only when the machine has enough disk and time budget for larger shard writes. Must be a positive integer. Invalid values fail with a diagnostic naming the variable.
RIPR_COMPACT_REPO_SEAM_CACHE_MAX_SEAMS100000Maximum seam count per shard in the compact repo seam cache. Larger compact cache entries are written as bounded shard files under the cache base directory. Raise this for large repos when the machine has enough disk and time budget for larger shard writes. Must be a positive integer. Invalid values fail with a diagnostic naming the variable.

Repo seam cache entries larger than the active limit are stored as a manifest plus shard files, with cache-store trace status such as sharded_ok_seams_135812_shards_7_limit_20000. Warm loads stitch the shards only when the manifest and every shard match the current cache key; missing or corrupt shards are ignored as cache corruption and the run recomputes instead of using partial evidence. cargo xtask cache report summarizes sharded families, largest shard sets, and orphan or incomplete shard sets. The cargo xtask cache gc --dry-run command still sees sharded entries because every shard lives under the cache base directory.

To relocate the cache to a different directory (e.g., for a read-only source checkout):

RIPR_CACHE_DIR=/var/cache/ripr ripr check --root . --format repo-exposure-json

To run a deliberately large Rust extraction diff on a larger runner:

RIPR_MAX_DIFF_CHANGED_RUST_LINES=5000 ripr check --root . --diff extraction.diff --json

To reduce full-cache shard counts for a repo on a machine with enough headroom:

RIPR_REPO_SEAM_CACHE_LIMIT=150000 ripr check --root . --format repo-exposure-json

To reduce compact-cache shard counts for that repo:

RIPR_COMPACT_REPO_SEAM_CACHE_MAX_SEAMS=200000 ripr check --root . --format repo-badge-json

Lane 1 audit reports may still emit limited run states when repo-exposure input is sampled, timed out, incomplete, stale, runner-failed, or skipped by a large-cache preflight. Sharding cache files does not turn those limited inputs into full repo truth.

ripr explain

Renders a single finding in human format.

ripr explain [--root PATH] [--base REV | --diff PATH] <finding-id | file:line>

The trailing positional argument selects the finding. Either form works:

  • A finding id, e.g. probe:src_lib.rs:predicate:bbaa2c25.
  • A file:line location, where the file matches the finding's path by exact match or path-suffix match.

ripr context

Emits a compact JSON context packet for one finding.

ripr context [--root PATH] [--base REV | --diff PATH]
             --at <finding-id | file:line>
             [--max-related-tests N] [--json]
FlagDefaultNotes
--at SELECTOR(required)Same selector grammar as explain.
--max-related-tests Nripr.toml reports.max_related_tests, otherwise 5Caps the number of related tests embedded in the packet.
--json(off)Forces JSON; context already returns JSON-shaped output, this flag is for parity with check.

ripr doctor

ripr doctor [--root PATH] [--json]

Reports local tooling and workspace shape. Takes no analysis-shaping flags.

Use --json for the same core checks as the human report when onboarding an agent, editor, or CI wrapper:

ripr doctor --root . --json

The JSON report and the human command share the root-directory, Cargo.toml, configuration, git/cargo/rustc, and language-runtime checks. A pass report exits zero; a fail report exits non-zero. This is machine-readable setup evidence, not a release or analysis gate decision.

The JSON report includes a runtime_probes array. A primary runtime probe is required: true only when its language is enabled in the effective configuration; a missing required runtime makes the report fail. Detected but disabled preview runtimes and optional test/package runners remain visible as required: false advisory results, so their absence does not make a Rust-only workspace fail.

doctor also reports the repository config status for the selected root:

Config: loaded ripr.toml
Config path: ./ripr.toml
Analysis mode default: deep
LSP seam diagnostics default: true
Suppressions path: .ripr/suppressions.toml

When no repo config exists, that is healthy and explicit:

Config: not found; using built-in defaults

Malformed config makes doctor fail with the config path and validation problem, but doctor does not print the config source text.

For an isolated configuration check that does not probe Git, Cargo, or rustc, use:

ripr config validate --root .

This uses the same ancestor-aware loader as analysis. A valid file prints ✓ ripr.toml valid; a malformed or policy-invalid file returns its path-qualified validation error. A missing ripr.toml follows the normal built-in-defaults path, including any existing root-level language detection rules, because that remains the normal first-run configuration.

ripr lsp

ripr lsp [--stdio] [--version]
FlagDefaultNotes
--stdioimplicitRun the language server over stdio LSP framing. This is the only supported transport today.
--version(off)Print the language server version and exit.

LSP runtime behavior is not configured by CLI flags; clients pass options via initializationOptions (next section), pushed workspace/didChangeConfiguration settings, or — when the client supports it — the server-originated workspace/configuration pull (see Configuration pull below).

LSP initializationOptions

When an LSP client starts ripr lsp --stdio, it can shape analysis by sending an initializationOptions object on the initialize request. The server reads seven keys; everything else is ignored. The schema lives in crates/ripr/src/lsp/config.rs.

KeyTypeDefaultEffect
baseRefstring"origin/main"Git base ref for editor-triggered diffs. Empty string disables base-ref diffing.
checkModestringripr.toml analysis.mode, otherwise "draft"One of instant, draft, fast, deep, ready. Unknown values fall back to the repo config/default.
includeUnchangedTestsbooleanripr.toml analysis.include_unchanged_tests, otherwise trueMirror of the CLI's --no-unchanged-tests (inverted).
seamDiagnosticsbooleanripr.toml lsp.seam_diagnostics, otherwise trueEnables repo seam evidence diagnostics in addition to diff-derived Finding diagnostics.
diagnosticProfilestringripr.toml lsp.diagnostic_profile, otherwise actionableactionable publishes only producer-backed bounded finding routes; full preserves audit/debug finding and seam visibility. Unknown initialization values fall back to the repository/default profile.
gitTimeoutMsnumber30000Cooperative deadline in milliseconds for each git invocation in the refresh path (Git-input probe and diff load). An exceeded deadline terminates the git process and commits a limited snapshot naming git_invocation_timeout; a timed-out probe fails closed as unresolved. Malformed initialization values are ignored (the default stays). No ripr.toml slot.
refreshDeadlineMsnumber600000Physical deadline in milliseconds for one whole refresh analysis attempt. An attempt that exceeds the deadline is cancelled cooperatively at analysis checkpoints and dropped fail-closed with the named deadline_exceeded outcome and an "analysis deadline exceeded" progress end — no limited snapshot is committed. A deadline cancel loses to an earlier supersede or client cancel (first-cancel-wins). Malformed initialization values are ignored (the default stays). No ripr.toml slot.

Initialization options are treated as explicit LSP settings and override ripr.toml. Defaults match CheckInput::default() when no repo config is present, except that LSP diagnostics render JSON-shaped data internally.

LSP configuration pull

When the client advertises capabilities.workspace.configuration = true, the server negotiates pull mode (RIPR-SPEC-0136) and requests the bounded ripr section once from the initialized handler:

workspace/configuration ← [{ "scopeUri": <selected root URI>, "section": "ripr" }]

The section carries the same seven governed keys as initializationOptions (baseRef, checkMode, includeUnchangedTests, seamDiagnostics, diagnosticProfile, gitTimeoutMs, refreshDeadlineMs) and nothing else. The response is validated before it is applied; a supported key with the wrong JSON type or an unknown enum literal fails the whole pull (fail-closed), while unrecognized keys are ignored.

Precedence per governed key in pull mode:

valid pulled setting > initialization option > ripr.toml > built-in default

workspace/didChangeConfiguration in pull mode does not apply pushed values; it invalidates the pulled layer and schedules one coalesced re-pull (responses for a superseded epoch are dropped). A re-pull that leaves the effective settings unchanged does not reschedule analysis.

Fallback, negotiated from capabilities only (never the client name):

  • pull — client answers workspace/configuration; behavior above.
  • push_fallback — no pull support, but the client advertises workspace/didChangeConfiguration; pushed values keep applying as today.
  • initialization_only — neither transport; only initializationOptions apply.

All three transports can supply exactly the same seven governed keys. The ripr.collectWorkspaceStatus payload discloses the negotiated configuration_mode, the per-field source of each governed value (pulled | initialization | repo | default), and the last pull state (pending until the first pull resolves, deferred when no single root is selected, applied, or failed with a typed kind and recovery route) under analysis_status.input_authority.

VS Code extension settings

The bundled VS Code extension exposes the settings below under the ripr.* namespace. The full schema lives in editors/vscode/package.json under contributes.configuration. The extension is responsible for turning these into LSP initializationOptions and server-launch arguments.

Server resolution

SettingTypeDefaultEffect
ripr.enabledbooleantrueEnables the VS Code saved-workspace server, diagnostics, hovers, status, and code actions. Set to false for an explicit disabled editor state without starting the language server.
ripr.server.pathstring""Absolute path to a ripr executable. Wins over bundled, downloaded, and PATH resolution.
ripr.server.argsstring array["lsp", "--stdio"]Arguments used to start the language server.
ripr.server.autoDownloadbooleantrueAuto-download a matching server binary when no configured, bundled, or cached one is available.
ripr.server.versionstring""Pin a specific server version. Empty means match the extension version.
ripr.server.downloadBaseUrlstring""Override the server manifest base URL (e.g. internal mirror). Empty uses GitHub Releases.

For the full resolution order (configured → bundled → cached → first-run download → PATH), see Server provisioning.

Analysis

SettingTypeDefaultEffect
ripr.check.modeenum: instant | draft | fast | deep | readydraftEditor-side analysis mode. Forwarded as initializationOptions.checkMode.
ripr.baseRefstring"origin/main"Git base ref used by editor diagnostics and the context commands. Forwarded as initializationOptions.baseRef.
ripr.includeUnchangedTestsbooleantrueInclude unchanged tests as static evidence. Forwarded as initializationOptions.includeUnchangedTests and the workspace/configuration pull.
ripr.seamDiagnosticsbooleantrueEnable saved-workspace repository seam diagnostics in addition to diff-derived findings. Forwarded as initializationOptions.seamDiagnostics.
ripr.diagnosticProfileenum: actionable | fullactionableSelect the bounded actionable or audit/debug diagnostic projection. Forwarded as initializationOptions.diagnosticProfile.
ripr.gitTimeoutMsnumber30000Cooperative per-invocation git deadline for the server refresh path. Served to the server through the workspace/configuration pull; an exceeded deadline commits a limited snapshot naming git_invocation_timeout.
ripr.refreshDeadlineMsnumber600000Physical deadline for one whole server refresh analysis attempt. Served to the server through the workspace/configuration pull; an exceeded deadline drops the attempt fail-closed with the named deadline_exceeded outcome (no limited snapshot is committed).

The editor default matches the CLI and direct LSP missing-config default: draft.

Diagnostics

SettingTypeDefaultEffect
ripr.trace.serverenum: off | messages | verboseoffLSP message tracing in the ripr output channel.

Commands

The extension contributes:

  • ripr.restartServer
  • ripr.selectWorkspaceRoot
  • ripr.showOutput
  • ripr.copyContext
  • ripr.copySuggestedAssertion
  • ripr.copyTargetedTestBrief
  • ripr.copyAgentPacketCommand
  • ripr.copyAgentBriefCommand
  • ripr.copyAfterSnapshotCommand
  • ripr.copyAgentVerifyCommand
  • ripr.copyAgentReceiptCommand
  • ripr.openRelatedTest
  • ripr.openSettings

These are not configured directly. They are surfaced through the command palette and from LSP code actions when diagnostics carry the required finding or seam data.

Repo policy files

Narrow, durable policy files live under .ripr/. They are not suppression mechanisms in the runtime sense — they are reasoned exceptions to repo-wide checks, and cargo xtask check-pr enforces that every entry has a named owner and a written reason.

.ripr/static-language-allowlist.toml

Files allowed to mention prohibited mutation-runtime vocabulary because they define the language boundary, document calibration plans, or describe agent rules. Validated by cargo xtask check-static-language. Source of truth for the prohibited terms themselves is forbidden_static_terms in xtask/src/main.rs.

Schema (parsed by parse_static_language_allowlist in xtask/src/main.rs):

schema_version = 1

[[allow]]
path = "AGENTS.md"
owner = "maintainers"
reason = "Agent instructions define the static-language boundary and must quote the prohibited terms verbatim."

[[allow]]
glob = "docs/**/*.md"
owner = "docs"
reason = "Nested documentation specs and ADRs may describe static-language policy and future calibration vocabulary."

Validation rules (all enforced; violations fail check-static-language):

RuleBehavior
schema_version = 1 requiredMissing or other values fail.
Exactly one of path or glob per [[allow]]Both or neither fail.
owner required, non-blankMissing or whitespace-only fails.
reason required, non-blankMissing or whitespace-only fails.
Duplicate matchersTwo entries with the same path or glob fail.
Absolute pathsEntries starting with / or matching <letter>: fail.
Backslash pathsEntries containing \ fail; use / separators.
Glob entries scopedCurrently only docs/*.md and docs/**/*.md are accepted; broader globs like *.md or **/*.md fail.
Exact paths must existpath = "..." entries that don't exist on disk fail at load time.

A legacy .ripr/static-language-allowlist.txt file is explicitly rejected; the loader fails with a clear migration message if both files are present.

.ripr/test_intent.toml

Positive declarations for intentionally smoke, duplicative, opaque, or otherwise-special tests. Each declaration carries an owner, a written reason, and an intent from a closed set. The original class emitted by the test-efficiency report is preserved — intent is additive metadata, never a replacement.

Validated by cargo xtask test-efficiency-report via parse_test_intent_manifest in xtask/src/main.rs.

schema_version = 1

[[test_intent]]
test = "cli_prints_help"
intent = "smoke"
reason = "CLI startup and help text smoke test."
owner = "devtools"

[[test_intent]]
test = "escapes_json"
path = "crates/ripr/src/output/json/formatter.rs"
intent = "business_case_duplicate"
reason = "These duplicate-looking tests document distinct escaping cases."
owner = "output"

Supported intent values:

IntentTypical use
smokeIntentional smoke-only test (CLI startup, help text).
business_case_duplicateStructurally similar tests that document distinct business cases.
opaque_external_oracleTest with an opaque oracle ripr cannot statically inspect.
integration_contractEnd-to-end contract test whose static class varies.
performance_guardTest exists to guard a performance characteristic.
documentation_exampleTest exists primarily as a documentation example.

Validation rules (all enforced; violations fail test-efficiency-report):

RuleBehavior
schema_version = 1 requiredMissing or other values fail.
test, intent, owner, reason requiredMissing or whitespace-only values fail.
intent must be one of the supported valuesUnknown intents fail with the supported-list message.
path optional, repo-relative, slash-separatedAbsolute paths and backslash paths fail at parse time.
path exists on diskMissing files fail at load time.
Duplicate (test, path) selectors rejectedFirst-declared line cited in the violation.
Unknown [[test_intent]] fields rejectedCatches typos and prevents silent shape drift.
Unmatched declarations rejectedA declared test/path selector that matches no test fails the report.
Ambiguous name-only selectors rejectedIf test = "..." matches multiple entries and no path is given, fail and list the candidates.

Future ripr+ will use the declared_intent metadata to exclude declared intentional test-efficiency findings from its count. See Badge policy.

.ripr/suppressions.toml

Accepted exceptions for exposure-gap or test-efficiency findings the team has agreed to carry as known debt. Suppressed findings remain visible in detailed reports — they only move from unsuppressed_* into suppressed_* in the badge counts.

Validated by ripr check --format badge-* (loaded relative to --root). Schema:

schema_version = 1

[[suppressions]]
kind = "exposure_gap"
finding_id = "probe:src/pricing.rs:88:predicate"
reason = "Covered by integration test in tests/billing/integration.rs that ripr cannot statically inspect yet."
owner = "billing"
expires = "2026-09-01"
scope = "seam:pricing::threshold"
created_at = "2026-01-01"
last_seen = "2026-05-01"
review_by = "2026-12-01"
expected_visibility = "suppressed_visible"
static_class = "weakly_exposed"
language = "rust"

[[suppressions]]
kind = "test_efficiency"
test = "cli_prints_help"
path = "tests/cli.rs"
reason = "The CLI help smoke test is intentionally broad and covered by CLI contract tests."
owner = "devtools"

Supported kind values (closed set): exposure_gap, test_efficiency.

RuleBehavior
schema_version = 1 requiredMissing or other values fail.
kind, owner, reason required and non-blankMissing or whitespace-only values fail.
kind = "exposure_gap" requires finding_idAnd rejects test.
kind = "test_efficiency" requires testAnd rejects finding_id; path is optional for disambiguation.
path repo-relative, slash-separatedAbsolute paths and backslash paths fail at parse time.
expires ISO YYYY-MM-DD if presentOther formats fail at parse time.
created_at, last_seen, review_by ISO YYYY-MM-DD if presentOther formats fail at parse time.
Unknown fields rejectedCatches typos.
Duplicate selectors rejectedSame finding_id (or (test, path)) twice fails.
Unmatched selectors surface as warningsSelector that matches no current finding is reported but does not fail the badge.
Expired suppressions do not applyThey surface as warnings on the badge so silent green-forever debt is impossible.

Policy-health metadata:

FieldMeaning
scopeReviewed scope for the durable exception. Avoid broad values such as repo, workspace, global, all, or *.
created_atDate the durable exception was created.
last_seenDate the suppressed finding was last reviewed or observed.
review_byDate by which the exception should be reviewed again.
expected_visibilityExpected reporting treatment, usually suppressed_visible.
static_classStatic evidence class covered by the suppression, such as weakly_exposed or reachable_unrevealed.
languageOptional evidence language, such as rust, typescript, or python.
language_statusRequired as preview for preview-language suppressions until an explicit policy promotes them.

ripr policy suppression-health reads the same manifest and writes target/ripr/reports/suppression-health.json plus Markdown. It flags missing owner, missing reason, stale review windows, overbroad scope, unknown selectors, missing policy-health metadata, and preview-language suppressions without language_status = "preview". The report is advisory and read-only; it does not create, delete, apply, or gate suppressions.

Suppressions and declared_intent are distinct concerns: intent is a positive declaration about test purpose; a suppression is an accepted exception or accepted debt. They can coexist on the same test (suppression wins for the badge count); the test-efficiency JSON shows both fields independently.

When suppressions affect the badge, the native JSON warnings array surfaces expired/unmatched selectors. The Shields projection always remains exactly four fields and never leaks warning text.

Analysis modes

Modes change how much of the workspace is loaded into the syntax index before classification. They do not change the meaning of any exposure class.

The default operator stance is draft: enough package-local context for a useful first scan without whole-workspace cost. instant is the cheapest fast-feedback mode, fast currently shares draft's package-local scope, and deep / ready are whole-workspace static scans.

ModeIndex scopeIntended use
instantChanged Rust files only.Editor-safe, cheapest feedback.
draftRust files in packages touched by the diff.Default local CLI scan.
fastSame package-local scope as draft for now.Draft PR scan; future bounded graph work lands here.
deepAll Rust files in the workspace.Manual or CI scan when wider static evidence is acceptable.
readyAll Rust files in the workspace.Static preflight before real mutation confirmation.

ready does not run mutants. It remains static exposure analysis until a calibration or mutation adapter is explicitly invoked.

Repo discovery defaults

Repo-mode commands discover Rust files from the selected --root, then apply the analysis mode scope above. Discovery skips directories that are normally generated, policy-only, fixture-only, or editor/package-manager state:

  • .git
  • target
  • .ripr
  • .direnv
  • fixtures
  • node_modules

When repo seam inventory decides whether a Rust file is production code, it also excludes tests, examples, benches, target, top-level fixtures, editor extension code, package-manager directories, and xtask automation. Test files can still be indexed as evidence when include_unchanged_tests = true; they are not treated as production seams.

Output formats

FormatSelectorWhen to use
humandefault, or --format human / --format textLocal terminal review.
json--json or --format jsonTools, editors, CI, agents. Versioned via schema_version. See Output schema.
github--format githubGitHub Actions annotations.

The context command always returns JSON-shaped output regardless of --format.

ripr.toml

ripr starts config discovery at the root passed by --root or by the LSP initialization root, then walks upward for the nearest ripr.toml. Discovery stops after checking an ancestor with a Cargo [workspace] table or at a .git boundary. Missing config is normal and preserves current built-in defaults. Unknown keys are errors so policy typos do not silently change analysis intent.

The example file (ripr.toml.example) is kept in sync with the supported v1 shape.

ripr init generates a profile aligned with RIPR-SPEC-0009. That generated profile is a materialized policy file, not an activation step: it records the same built-in defaults so a team can review, commit, and tune them. It does not enable runtime mutation execution or CI blocking policy.

[analysis]

KeyTypeDefaultEffect
modeenum: instant | draft | fast | deep | readydraftDefault analysis mode when not set by a CLI flag or LSP initialization option.
include_unchanged_testsbooleantrueWhether unchanged tests may be indexed as static evidence.

[oracles]

These settings adjust repo policy for oracle shapes whose strength can vary by team convention. Exact value and exact error-variant assertions remain strong; smoke-only assertions remain smoke.

KeyTypeDefaultEffect
snapshot_strengthenum: strong | medium | weak | smoke | none | unknownmediumStrength assigned to snapshot-style oracles.
mock_expectation_strengthsame enummediumStrength assigned to mock/side-effect expectation oracles.
broad_error_strengthsame enumweakStrength assigned to broad error checks such as is_err().

[severity.findings]

Finding severities affect human output, JSON severity, GitHub annotations, and LSP Finding diagnostics. Valid values are info, warning, and note. Use suppressions for accepted debt; Finding severities cannot be off.

KeyDefault
exposedinfo
weakly_exposedwarning
reachable_unrevealedwarning
no_static_pathwarning
infection_unknownwarning
propagation_unknownnote
static_unknownnote

[severity.seams]

Seam severities affect LSP seam diagnostics. Valid values are off, info, warning, and note. Classes set to off do not publish diagnostics.

KeyDefault
strongly_grippedoff
weakly_grippedwarning
ungrippedwarning
reachable_unrevealedwarning
activation_unknowninfo
propagation_unknowninfo
observation_unknowninfo
discrimination_unknowninfo
opaqueinfo
intentionaloff
suppressedoff

[lsp]

KeyTypeDefaultEffect
seam_diagnosticsbooleantrueDefault for bounded saved-workspace repo seam diagnostics. LSP initializationOptions.seamDiagnostics still wins.
diagnostic_profileenum: actionable | fullactionableactionable suppresses exposed, unknown, opaque, and route-less diagnostics; full preserves the audit/debug projection. LSP initializationOptions.diagnosticProfile still wins.

[reports]

KeyTypeDefaultEffect
max_related_testsinteger5Default cap for context packets and server-side collect-context commands.

[suppressions]

KeyTypeDefaultEffect
pathrepo-relative path.ripr/suppressions.tomlBadge renderers load suppressions from this path. Absolute paths, .., and backslashes are rejected.

[languages]

KeyTypeDefaultEffect
enabledarray of strings["rust"]Language adapters the analysis pipeline will dispatch to. Valid values: rust, typescript, python, perl. Unknown values and duplicate entries are rejected. TypeScript covers .ts, .tsx, .js, and .jsx; Python covers .py. Perl consumes externally-produced ripr-perl-facts-v1 packets and does not parse Perl source directly. Supply one with --perl-facts <path>, or configure a managed exporter under [perl]; without a packet or available exporter, Perl analysis is unavailable. Rust remains the reference adapter and the only adapter that may be stable per RIPR-SPEC-0026; TypeScript, Python, and Perl remain preview adapters. See Support Tiers and Campaign 31, #1379.

[languages.rust] configures the stable Rust adapter:

KeyTypeDefaultEffect
generated_file_patternsarray of strings[]Additional Rust generated-source globs. Built-in generated names and gen/, generated/, and out/ directories remain excluded. A pattern without / matches any filename; a pattern with / matches the repository-relative path. * matches within one path segment, ? matches one character, and ** matches zero or more path segments. Empty, duplicate, absolute, parent-traversing, drive-prefixed, and backslash-containing patterns are rejected.

For example:

[languages]
enabled = ["rust"]

[languages.rust]
generated_file_patterns = ["*.gen.rs", "src/generated/**/*.rs"]

[languages] controls runtime routing for the selected repository. The ripr binary must also be built with the corresponding adapter feature. The default published build includes the preview adapter features, while a Rust-only build can omit them:

cargo build -p ripr --no-default-features --features lang-rust

If repo config enables a language that is not available in the current binary, configuration fails closed with a message naming the missing Cargo feature, for example lang-python. The editor reports that configuration problem instead of publishing phantom preview diagnostics.

[typescript]

KeyTypeDefaultEffect
resolve_tsconfig_pathsbooleanfalseResolve TypeScript path aliases from tsconfig.json or jsconfig.json during owner-to-test discovery.

[perl]

Perl is a fact-packet consumer. It does not parse .pm, .pl, .t, or .psgi source directly. Use either an explicit packet or a managed exporter:

ripr check --perl-facts target/ripr/reports/perl-facts.json
[perl]
producer = "perl-ripr-facts"
# `perllsp` and `perl-lsp` are accepted compatibility wrappers.

Managed mode invokes the configured external exporter when it is available; otherwise the Perl language run is reported as unavailable while other enabled languages continue. The accepted managed producer values are perl-ripr-facts, perllsp, and perl-lsp.

KeyTypeDefaultEffect
producerstringnoneSelects managed producer mode. Accepted values are perl-ripr-facts, perllsp, and perl-lsp.
executablepathnoneOverrides the Perl facts exporter executable path.
timeout_msinteger30000Maximum time in milliseconds for the managed producer invocation.
cache_dirpathnoneDirectory for generated Perl fact packets.

To evaluate preview languages, keep Rust enabled and add only the preview adapters the repo wants to inspect:

[languages]
enabled = ["rust", "typescript", "python"]

[profiles.bun_ub]

The Bun UB profile is an opt-in advisory profile for Bun stable-byte review. It records where TypeScript-family integration tests and Bun bridge hints live so operators can run the calibrated Blob / ArrayBuffer cross-language preview without changing Rust defaults.

[languages]
enabled = ["rust", "typescript"]

[profiles.bun_ub]
test_roots = [
  "test/js/**/*.test.ts",
  "test/js/**/*.test.js",
]
bridge_hints = "ripr.bun.bridge.toml"
KeyTypeDefaultEffect
test_rootsnon-empty array of repo-relative glob stringsnone; required when the profile is presentNames the TypeScript-family Bun integration tests to inspect. JavaScript test files are covered by the typescript adapter.
bridge_hintsrepo-relative pathnone; required when the profile is presentPoints to the opt-in Bun TS-to-Rust bridge-hint file.

This profile is read-only configuration. It does not enable TypeScript by itself, run tsc, start tsserver, execute Bun/Jest/Vitest, generate tests, edit source, contribute to gates, badges, baselines, or RIPR Zero, or promote TypeScript/JavaScript out of preview. ripr doctor reports whether the profile is configured and repeats that authority boundary.

See Language adapter preview workflow for how to read preview labels, static limits, generated-CI grouping, editor projection, and rollback. See the Bun UB TypeScript preview runbook for the calibrated stable-byte review loop and the exact advisory actions for missing discriminators, token-only evidence, unknown bridges, and unresolved FFI panic-boundary visibility.

Worked example

[analysis]
mode = "draft"
include_unchanged_tests = true

[oracles]
snapshot_strength = "medium"
mock_expectation_strength = "medium"
broad_error_strength = "weak"

[severity.findings]
weakly_exposed = "warning"
static_unknown = "note"

[severity.seams]
weakly_gripped = "warning"
opaque = "info"

[lsp]
seam_diagnostics = true

[reports]
max_related_tests = 5

[suppressions]
path = ".ripr/suppressions.toml"

[languages]
enabled = ["rust"]

Precedence

For CLI commands:

CLI flag  >  ripr.toml  >  CheckInput::default()

For LSP, the negotiated configuration transport decides (see "LSP configuration pull" above):

pull mode:      valid pulled setting  >  LSP initializationOptions  >  ripr.toml  >  CheckInput::default()
other modes:    LSP initializationOptions  >  ripr.toml  >  CheckInput::default()

See also