Skip to content
Open the dashboard
Developer docs

Check your changes

Developers11 min read

groundrule check runs your rules’ checks against your code and tells you what to fix. By default it looks only at what you changed, so existing problems don’t block your work. This page explains what it checks, which findings fail it, how to read the output, and every option.

Terminal window
npx @groundrule/cli check

Only standards with a check (a regular expression, a file rule, a dependency rule, and so on) can produce findings. Standards that are guidance only reach your agents through sync, and check counts them as guidance.

Command What is checked
check Your uncommitted work: staged and unstaged changes compared with your last commit, plus new files git doesn’t ignore.
check --base <ref> Everything since your branch left <ref> (git’s merge base), plus your uncommitted work. Use it in CI and before opening a pull request: --base origin/main.
check --all Every file in the repository, changed or not. The header says “(full audit)”.

When checking changes, checks look only at the changed files. Change-set checks (such as “entity changes need a migration”) also see which files the change deleted.

enforcement.scope in .groundrule/config.yaml sets the default:

scope Without --all, a finding is “new” when…
changed-lines (default) it’s on a line you added or changed.
changed-files it’s anywhere in a file you changed.
all always: check audits every file, as with --all.

Other findings in the files you touched are legacy findings: they were there before your change. Findings that aren’t tied to a line, such as a missing lockfile, also count as legacy when checking changes. --all treats every finding as new.

Outside a git repository there’s no change to compare, so check warns “Not a git repository, so there is no change to compare. Checking every file instead.” and audits everything.

Groundrule never checks its own files: .groundrule/, the managed blocks in your agent files, and the Cursor and Copilot files it owns.

A finding fails the check when all of these hold:

  1. It’s a violation, not a “possible” finding (see How to read a finding).
  2. No active exception covers it.
  3. It’s new, or enforcement.legacy is enforce.
  4. Its severity is at or above enforcement.failOn.
failOn Fails on
blocker (default) blockers
warning blockers and warnings
none nothing. The report still lists every finding.

Advisory and info findings never fail a check. Override the config for one run with --fail-on blocker|warning|none.

legacy What happens to legacy findings
report (default) Counted, hidden from the terminal report, and never fail. --verbose shows them.
ignore Dropped from the report.
enforce Treated like new findings: they can fail the check.

In a repository connected to the platform, each rule’s stage decides what check does with it:

Stage In check
Observe Not run. Results are recorded only by scans.
Teach Not run. The rule reaches your agents through sync.
Advise Run. A blocker is reported as a warning, so with the default failOn: blocker it never fails the check.
Enforce Run at the rule’s own severity. With the default failOn: blocker, its blockers fail the check.

That’s why check may count fewer standards than sync writes: for example 43 checked, 79 in the agent files.

If you set failOn: warning, warnings from Advise rules fail the check too.

In an offline repository there are no stages. Every standard is checked at its own severity.

groundrule check · 43 standards · 14 files (full audit)
✕ GHA-003 Do not interpolate untrusted event data into scripts
BLOCKER · deterministic (regex)
.github/workflows/ci.yml:8
Untrusted event data interpolated into a script
8: - run: echo "${{ github.event.pull_request.title }}"
→ Move the expression into the step's env: block (e.g. TITLE: ${{ github.event.issue.title }}) and use "$TITLE" in the script, always double-quoted.
→ groundrule explain GHA-003
⚠ TS-002 No deprecated HTTP and UUID packages
warning · deterministic (dependencies)
package.json:2
Forbidden dependency "request"
package.json declares request (npm), which matches "request"
→ Use fetch or crypto.randomUUID() instead.
→ groundrule explain TS-002
Failed ✓ 31 passed ✕ 1 failed ⚠ 5 warnings 6 not applicable · 0.0s

The first line counts the standards checked and what was checked: “N files changed”, “N files changed vs origin/main”, or “N files (full audit)”.

Blocking findings come first, then the rest by severity. Each standard with findings gets one block:

Part Example Meaning
Icon ✕ ⚠ ? ✕ fails the check. ⚠ is reported but doesn’t fail. ? is a possible finding the check isn’t confident about; it never fails.
ID and title GHA-003 Do not interpolate… The standard.
Severity BLOCKER, warning In capitals when the finding fails the check.
Source deterministic (regex) How it was found, and by which check. See below.
Location .github/workflows/ci.yml:8 File and line. May be followed by possible, legacy, or excepted by EX-1.
Message and evidence 8: - run: echo … What was found, with up to two lines of evidence, such as the offending line.
Fix → Move the expression… How to fix it.
Explain → groundrule explain GHA-003 The command that shows the full rule.

The source label says how the finding was produced:

Label Checks Meaning
deterministic regex, files, dependencies, change-set The same code always gives the same result. No AI.
static analysis semgrep A Semgrep rule. Needs Semgrep installed; without it, the rule is “not evaluated”.
AI llm Not yet available. Rules with an AI check are reported as not evaluated.

At most five locations are shown per standard. The rest are summed up as “… N more in M files (show all with –verbose)”.

After the findings, you may see:

  • ○ 3 legacy findings not introduced by this change · 1 finding covered by exceptions (show with --verbose);
  • – <ID> not evaluated: <reason>, when a check couldn’t run, for example because Semgrep isn’t installed.

The last line gives the verdict, Passed or Failed, then counts: passed, failed, warnings, not evaluated, guidance (◇), and not applicable (rules whose languages or frameworks aren’t in this repository).

Option What it does
--base <ref> Compare against a branch or commit, such as origin/main. The ref must exist locally: fetch it first.
--all Check every file, not only changes.
-f, --format <format> terminal (default), json, sarif, or markdown.
-o, --output <file> Write a json, sarif or markdown report to a file instead of printing it. The CLI prints “Wrote sarif report to groundrule.sarif (failed).”
--summary <file> Also append a Markdown summary to a file, such as $GITHUB_STEP_SUMMARY. Works with any format.
--fail-on <severity> blocker, warning, or none. Overrides enforcement.failOn for this run.
--only <ids> Check only these standards, comma-separated, such as --only GHA-003,TS-001. Case doesn’t matter.
--verbose Also show legacy findings, findings covered by exceptions, and every location.

Examples:

Terminal window
# What a pull request adds, compared with main
git fetch origin main
npx @groundrule/cli check --base origin/main
# One rule, everywhere
npx @groundrule/cli check --all --only SEC-001
# A SARIF file for code scanning, and a summary for the job page
npx @groundrule/cli check --base origin/main -f sarif -o groundrule.sarif --summary "$GITHUB_STEP_SUMMARY"
  • terminal: the report above. Colors turn off when the output isn’t a terminal or NO_COLOR is set.

  • json: the full result, including legacy and excepted findings, for scripts.

  • sarif: SARIF 2.1.0, for GitHub code scanning and other tools. Findings covered by exceptions are marked as suppressed.

  • markdown: a table for pull-request comments and job summaries. Real output:

    ### Groundrule · ⚠ Passed with 1 warning
    | | Standard | Finding | Where | Source |
    |---|---|---|---|---|
    | ⚠ | **REACT-006** Give every img an alt attribute | <img> without an alt attribute<br>**Fix:** Add alt="what the image shows" for meaningful images, or alt="" if it is decorative. | `src/components/Hero.tsx:1` | deterministic |
    ✓ 17 passed · ◇ 14 guidance
    <sub>Groundrule 0.1.0 · run `groundrule explain <ID>` for details</sub>

Each format is described field by field in Output formats.

Code Meaning
0 Passed: no finding fails the check. Warnings may still be listed.
1 Failed: at least one finding fails the check.
2 The check couldn’t run: a configuration error, a ref that can’t be compared (Cannot compare against "origin/main". Fetch it first (e.g. git fetch origin main).), a sign-in problem, or a wrong option.

When a rule shouldn’t apply to some code, record a time-boxed exception in .groundrule/exceptions.yaml instead of turning the rule off:

apiVersion: groundrule.dev/v1alpha1
kind: ExceptionList
spec:
exceptions:
- id: EX-1
standard: DOCKER-001
paths: ["tools/ci-image/Dockerfile"]
reason: Build-only image; never runs as a service.
requestedBy: maya@acme.example
approvedBy: team:platform
expires: 2026-12-31
remediation: Move the build to the shared runner image.
Field Required Meaning
id Yes EX- and a number, such as EX-1042.
standard Yes The standard’s ID.
paths No Globs for the files it covers. The default ** means the whole repository.
reason Yes Why the rule doesn’t apply here.
expires Yes A date (YYYY-MM-DD). The exception works through the end of that day (UTC).
requestedBy, approvedBy, remediation No Who asked, who approved, and the plan to remove the exception.

Findings it covers are shown as excepted by EX-1 with --verbose, and never fail the check. When an exception expires, every command warns “EX-1 for DOCKER-001 expired on 2026-12-31 and no longer applies.”, and the findings count again. groundrule explain <ID> lists a rule’s exceptions. Commit the file, so the exception is reviewed like any other change.

More: Overrides and exceptions.

Add a git hook, so you see findings before CI does. For example, in .git/hooks/pre-push:

#!/bin/sh
npx @groundrule/cli check --base origin/main

Make it executable with chmod +x .git/hooks/pre-push. Coding agents run npx @groundrule/cli check themselves: the agent files tell them to.