GeezerVison is showing orieg.github.io — Discipline — Universal CI/CD Diff Sentinel & AI Coding Agent Gatekeeper
Why Discipline?
Autonomous AI agents in an iterate-until-green loop optimize for passing tests, not preserving invariants. Discipline halts erosion at the gate.
🌳
Tree-Sitter AST Diff Inspection
Unlike fragile regex checks that trigger on comments or log messages, Discipline parses the complete AST of changed functions against the merge base. It counts executable assertions and distinguishes real invariants from dead code: an assertion under if false or after a return counts for nothing, and a property-test budget inside a string or comment is not a budget. Fourteen packs: Rust, Python, JavaScript / TypeScript, Java, Go, PHP, C, C++, C#, Ruby, Kotlin, Swift, Scala and Objective-C, plus a section parser for PHPT test files.
tree-sitter-rust
tree-sitter-python
tree-sitter-javascript
🤖
AI Coding Agent Guardrails
Flags silent test erosion: assertion count reductions (e.g. assert_eq! → assert!), test suppression and retries (#[ignore], @pytest.mark.flaky, it.skip), tautological or mock-only tests (assert!(true), toHaveBeenCalled alone), stealth test deletions, stub bodies even when padded with a log line (todo!(), raise NotImplementedError), swallowed errors (except: pass, a handler that only logs, let _ = fallible(), rescue nil, PHP @call()), a snapshot added for a test that already existed, a repointed lockfile entry, hard-coded sleeps and not-null-only assertions, and injection text in comments, PR descriptions or agent instruction files.
Claude Code
Codex CLI
Cursor
Aider
Copilot CLI
Antigravity
Qwen Code
OpenCode
MCP
🔒
Fail-Closed Engineering
If the base ref or the merge base cannot be resolved (a clone too shallow to reach it, a branch never fetched), or a configuration file is malformed, Discipline exits with code 2. It never yields a false-positive pass on broken pipelines or empty diff outputs. The trust boundary is guarded too: a change cannot switch its own run to advisory, disable config-integrity, loosen tsconfig.json / ruff.toml / [lints] / coverage floors, mask the CI step, or add a postinstall hook or a .npmrc registry that runs on every install, without a scoped directive.
exit 0: pass
exit 1: violation
exit 2: error
📊
Native MR & PR Reporting
Discipline emits industry-standard reports without external plugins: GitLab Code Quality diffs, JUnit test widgets, GitHub PR comments, GitHub Actions Annotations, and SARIF security tabs.
SARIF 2.1.0
JUnit XML
Code Climate JSON
Audit the Escape Hatches
Every waiver, loosened option, grandfathered finding and protected-path edit your merged history carried, ranked by what it could hide, with a link to the exact line on your forge.
Ranked, not rawSignals such as a loosened guard gate, a loosening pushed without a pull request, or one never tightened back lead the page, each with the next thing to check.
Honest about gapsWith --forge it reads owner ratifications, reviews and pull-request descriptions; with --replay, which waivers lifted anything. Whatever it cannot tell, such as which agent made a change, is marked not checked, never shown as clean.
One page, linked to the sourcediscipline audit --format html writes a self-contained page that loads nothing. Each row opens the file and line, the diff or the commit on GitHub, GitLab, Gitea or Forgejo.
Results
Discipline 0.32.0 blocked 17% of merged agent pull requests; a simple baseline rule that blocks any change to a test, CI or configuration file blocked 56%. 84% of discipline's blocks are correct (likely 76–90%), against 2% for that rule. Each agent edit is checked in a few seconds. It does not yet catch most test gaming: on a public benchmark it blocked 8% of agents' edits that made impossible tests pass, short of the 80% set in advance.
How often does it block?
Were those blocks right?
AI models graded every finding, not people. Two models from different vendors agreed on 266 of 276; a third, from one of those vendors, settled the other 10. One pull request holds 52 of the 122 findings, so the per-pull-request range is wider.
How long does each check take?
Does it catch test gaming?
Not yet, mostly (#838).
The verdict is fixed by the release chosen before the study, 0.26.0: it fails its bar. 75 of the 138 cases are tests the benchmark itself broke (they do not parse, or the changed line sits outside any test), so a diff check has nothing to see; on the other 63, discipline caught 8. The later builds catch more, but they were fixed using these same cases, so only fresh cases can show whether that holds. The simple rules catch more because they block every test edit; on real pull requests the file rule blocked 56%.
Judge it on your own repository
discipline replay --last 50 shows what discipline would have blocked in your last 50 merged changes, without blocking anything.
Platform Integration Guides
First-class support across all enterprise CI/CD systems, container runtimes, and local developer hooks.
Composite GitHub Action writing workflow annotations, a step summary and step outputs. comment: true adds one pull-request comment (needs pull-requests: write); DISCIPLINE_REPORT_SARIF plus an upload step adds code scanning (guide).
# .github/workflows/discipline.yml
name: Discipline Sentinel
on:
pull_request:
types: [opened, synchronize, reopened, edited]
permissions:
contents: read
jobs:
sentinel:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0 # Ensure git merge-base is reachable
- name: Run Discipline Sentinel
uses: orieg/discipline@f3d6d4a0bc4f1f60f63fa101aa27e1a17d0d5276 # v0.32.0
The action is pinned by commit SHA because the default ci-integrity gate rejects a newly added action that is not, @v0 included. A SHA ref runs the binary of the release that commit names. The step fails on errors only; add fail_on_warnings: true under with: once the warnings are fixed or baselined.
Native GitLab CI/CD integration. Populates GitLab Merge Request Code Quality diffs and JUnit test reports.
# .gitlab-ci.yml including the Discipline job template, pinned to a release
include:
- remote: https://raw.githubusercontent.com/orieg/discipline/v0.32.0/templates/discipline.gitlab-ci.yml
inputs:
stage: test
allow_failure: false
# Or standalone job definition:
discipline:gate:
stage: test
image:
name: ghcr.io/orieg/discipline:v0
entrypoint: [""]
variables:
GIT_STRATEGY: clone
GIT_DEPTH: 0
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
before_script:
- git fetch origin $CI_MERGE_REQUEST_TARGET_BRANCH_NAME || true
script:
# Under $GITLAB_CI, automatically outputs gl-codequality.json and junit.xml
- discipline check
artifacts:
reports:
codequality: gl-codequality.json
junit: junit.xml
paths:
- gl-codequality.json
- junit.xml
when: always
Runs under forgejo-runner from .forgejo/workflows/, with the base ref and pull-request event detected automatically. edited re-runs the check when the pull-request description (and a directive in it) changes.
# .forgejo/workflows/discipline.yml
name: Forgejo Sentinel Gate
on:
pull_request:
types: [opened, synchronize, reopened, edited]
jobs:
discipline:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false # the job's token can write
- name: Run Discipline
uses: https://github.com/orieg/discipline@f3d6d4a0bc4f1f60f63fa101aa27e1a17d0d5276 # v0.32.0
# Runner without internet access: with: { binary_path: /opt/discipline/discipline }
Runs under self-hosted Gitea act_runner. The action itself is shell-only; the runner image needs git, curl (or wget) and Node.js for actions/checkout. Reference the action by its full URL, since the runner resolves a bare owner/repo against your own instance.
# .gitea/workflows/discipline.yml
name: Gitea Sentinel Gate
on:
pull_request:
types: [opened, synchronize, reopened, edited]
jobs:
discipline:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false # the job's token can write
- name: Run Discipline
uses: https://github.com/orieg/discipline@f3d6d4a0bc4f1f60f63fa101aa27e1a17d0d5276 # v0.32.0
# Runner without internet access: with: { binary_path: /opt/discipline/discipline }
Kubernetes-native pre-merge gating for GitOps and continuous deployment pipelines.
# Argo Workflow DAG Task Reference
- name: run-discipline-gate
templateRef:
name: discipline-sentinel
template: discipline-gate
arguments:
parameters:
- name: repo-url
value: "https://github.com/org/repo.git"
- name: target-branch
value: "main"
- name: source-branch
value: "feat/my-pr"
- name: pr-body
value: ""
- name: discipline-image
value: "ghcr.io/orieg/discipline:v0"
Non-root, minimal static Alpine image for local containers or custom CI runners.
# Pull official static container image
docker pull ghcr.io/orieg/discipline:latest
# Mount repository and execute diff check against origin/main
docker run --rm \
-v "$PWD":/workspace \
-w /workspace \
ghcr.io/orieg/discipline:latest check --base origin/main
Git hook configuration preventing non-compliant commits before they reach remote branches.
# .pre-commit-config.yaml
repos:
- repo: https://github.com/orieg/discipline
rev: v0.32.0
hooks:
- id: discipline # Compiles via cargo
# - id: discipline-system # Or uses binary on PATH
Direct command-line execution for local developers, script automation, and self-checks.
# Run full gate check against merge-base
discipline check --base origin/main
# Run check on staged changes before committing
discipline check --staged
# Inspect registry of gates and current configuration
discipline gates
# Run embedded verification controls
discipline self-test
# What a gate checks, and what your last 50 merged changes would have hit
discipline explain assertion-reduction
discipline replay --last 50
Tell the agent while it edits: after an edit that weakens a test, the hook hands the agent the finding and its repair, judged by the default branch's configuration so the change cannot switch its own check off. Where the agent supports it, a pre-tool entry refuses an edit into another worktree before it runs, and a session-start entry takes this worktree's lease.
# Claude Code, Codex, Cursor, Aider, Copilot CLI, agy, Qwen Code or OpenCode
discipline hook install --agent claude-code
# Any MCP client: register the server (tools: check_diff, list_gates, explain_finding)
claude mcp add discipline -- discipline mcp
# What the agent reads after an edit that weakens a test (exit 2)
### Issue 1 [assertion-reduction/assertions-reduced]: Assertion Count Decreased In Existing Test
- Location: `tests/cart.rs:6`
- Problem:
```text
Test `adds`: effective assertions dropped from 2 to 0.
```
- Repair: Restore the assertions that were removed or weakened ...
Every agent's contract, the MCP registration for each client, and what a change cannot do to its check:
Agent Hooks and
MCP Server.
Azure Pipelines, Bitbucket Pipelines, CircleCI and Jenkins templates, and the full wiring for each platform: CI/CD Platform Integration Guide.
Active Gate Reference
Every gate operates with fail-closed semantics, truthful examination counts, and explicit remediation instructions.
Showing 47 of 47 gates
| Gate ID |
Category |
Default |
Languages |
Rule Description |
| agents-md |
Agent Guard |
Warning |
Any |
AGENTS.md exists; CLAUDE.md / GEMINI.md do not fork it |
| assertion-reduction |
Agent Guard |
Error |
Rust, PHPT, Python, JS/TS, Java, Go, PHP, C/C++, C#, Ruby, Kotlin, Swift, Scala, Objective-C |
assertion count / strength must not drop in an existing test |
| vacuous-tests |
Agent Guard |
Error |
Rust, PHPT, Python, JS/TS, Java, Go, PHP, C/C++, C#, Ruby, Kotlin, Swift, Scala, Objective-C |
new tests must carry a non-tautological assertion |
| ignored-tests |
Agent Guard |
Error |
Rust, PHPT, Python, JS/TS, Java, Go, PHP, C/C++, C#, Ruby, Kotlin, Swift, Scala, Objective-C |
tests must not be newly #[ignore]d or skipped without directive |
| unsafe-safety-comment |
Agent Guard |
Error |
Rust |
unsafe blocks / impls carry a // SAFETY: comment |
| deletion-rationale |
Agent Guard |
Error |
Any |
deleted files and removed tests need a scoped removes: rationale |
| time-estimates |
Hygiene |
Warning |
Any |
no calendar / duration estimates in markdown or the PR body |
| pii |
Hygiene |
Error |
Any |
no home paths, LAN IPs, denylisted hostnames, or fixed-format token literals (private-key headers; AWS, GitHub, Slack, OpenAI and Anthropic token formats; literal bearer headers) in tracked text: literal format matching, not credential scanning |
| agent-scratch |
Hygiene |
Error |
Any |
agent scratch state is never tracked |
| shell-secrets |
Hygiene |
Error |
shell, docker, workflows |
no secrets on command lines, fixed-format token literals, or unverified piped scripts in shell, docker, or CI files: literal pattern matching, not secret scanning |
| issue-link |
Hygiene |
Off |
Any |
PR title or description links a tracking issue (#123, Fixes #123) |
| review-threads |
Hygiene |
Off |
Any |
the pull request has no unresolved review thread |
| ratified-paths |
Agent Guard |
Off |
Any |
edits to protected paths carry an owner's ratification on an issue the pull request closes |
| citation-metadata |
Hygiene |
Error |
Any |
CITATION.cff and .zenodo.json are valid, agree with each other, and cite the concept DOI |
| commit-provenance |
Hygiene |
Off |
Any |
commits carry the required trailers; an agent-produced commit carries a review by someone else |
| config-integrity |
Integrity |
Error |
Any |
a change cannot weaken its own discipline.toml without a token |
| stub-bodies |
Agent Guard |
Error |
Rust, Python, JS/TS, Java, Go, PHP, C/C++, C#, Ruby, Kotlin, Swift, Scala, Objective-C |
added functions are not stubs; existing bodies are not replaced by todo!() / NotImplementedError / return null |
| harness-tampering |
Agent Guard |
Error |
Go, Python, JS/TS, Rust |
a file the test runner loads does not make a failing run pass |
| error-swallowing |
Agent Guard |
Error |
Rust, Python, JS/TS, Java, Go, PHP, C/C++, C#, Ruby, Kotlin, Swift, Scala, Objective-C |
no new empty error handler or discarded Result outside tests |
| instruction-smuggling |
Agent Guard |
Error |
any (invisible characters, instruction files); Rust, Python, JS/TS, Java, Go, PHP, C/C++, C#, Ruby, Kotlin, Swift, Scala, Objective-C and prose files (phrases) |
no invisible Unicode, unreviewed agent-instruction edits, or instruction-like text in comments and prose |
| build-hooks |
Integrity |
Error |
package.json, build.rs, setup.py, .npmrc, .pypirc, pip.conf, .cargo/config.toml, .env* |
install and build hooks that gain network or shell access, and package-manager configuration edits, need a token |
| toolchain-config |
Integrity |
Error |
tsconfig, ruff, mypy, pytest (addopts flags and -k / -m selection), coverage, flake8, Cargo lints, rustflags, nextest, eslintrc, golangci, jest and vitest (JSON or a literal object), package.json test scripts, codecov, phpstan, phpunit, and compiler warning flags in Makefile, CMake, setup.py and build.rs |
compiler, linter, type-checker, test-runner and coverage configuration cannot be loosened without a token |
| sandbox-config |
Integrity |
Error |
Claude Code, Codex, Gemini CLI, Qwen Code, OpenCode, Cursor and Copilot CLI settings, MCP server lists, devcontainer.json, Docker Compose, CI job and service containers |
a change cannot widen an agent's permissions or sandbox, or a container's isolation, without a token |
| scope-confinement |
Agent Guard |
Off |
Any |
changes stay inside authorized paths |
| suppression-delta |
Agent Guard |
Warning |
per pack |
newly added linter / compiler suppression annotations |
| provenance-tags |
Hygiene |
Off |
Any |
published numerics carry (measured|target|projected) |
| ci-integrity |
Integrity |
Error |
Any |
workflow weakening: continue-on-error, || true, unpinned actions |
| ci-skip-set |
Integrity |
Error |
Any |
rollup skip set matches each job's if: under the observed filter outputs |
| test-floor |
Integrity |
Error |
Any |
test-count ratchet read from the base ref |
| golden-output |
Integrity |
Error |
Any |
prevents stealth edits to committed golden/test output files without explicit override |
| dependency-delta |
Integrity |
Error |
Any |
manifest diff inspection: zero wildcards, source/license allowlists, and deny.toml verification |
| test-budget |
Integrity |
Error |
Rust, Python, JS/TS, Go, any |
property-test and fuzz effort ratchet (cases, shrink iters, fuzztime, seed corpus) |
| pr-checklist |
Hygiene |
Off |
Any |
ticked PR checkboxes and declared touched surfaces are reconciled against the diff |
| command |
Verification |
Error |
Any |
fail-closed wrapper for any tool: zero-tests guard, canary, count ratchet |
| sanitizers |
Verification |
Off |
Rust, C/C++ |
ASan / TSan preset with audited suppressions and a race canary |
| msrv |
Quality |
Off |
Rust |
cargo check under the pinned MSRV |
| miri |
Verification |
Off |
Rust |
Miri tiers with zero-tests guard |
| unsafe-budget |
Verification |
Off |
Rust |
unsafe count ratchet |
| bench-regression |
Performance |
Warning |
Rust, Go, Python, C/C++ |
benchmark drift via harness adapters (deterministic counts or BCa intervals) |
| archive-contents |
Integrity |
Off |
Any |
distribution archive must contain required paths and zero forbidden developer artifacts |
| manifest-sync |
Integrity |
Off |
Any |
reconcile git-tracked files against packaging manifest declarations |
| version-lockstep |
Integrity |
Off |
Any |
version declarations across headers, manifests, and files must remain in lockstep |
| gate-command-lint |
Agent Guard |
Off |
markdown, shell |
reject vacuous, masked, or inverted verification commands in plan files |
| mechanism-sections |
Hygiene |
Off |
markdown |
require (inferred) or (verified: <target>) evidence tags in root cause sections |
| citation-anchors |
Hygiene |
Off |
markdown, any |
verify path:line@sha citations against quoted text and git history, and relative Markdown links against the tree |
| claim-labels |
Hygiene |
Off |
markdown, any |
require RUN or READ labels on verification claims in PR bodies, commits, and Markdown |
| companion-edit |
Integrity |
Off |
Any |
a change to one path set must also change its companion path set |
Audit-Trailed Override Directives
Legitimate deletions or changes are authorized through explicit, scoped directives in the PR description or commit message; on a push to the default branch, the merged pull request's body is read through the forge (merged-pr-body), because a squash or rebase merge drops it from the commit message. Every applied override is recorded in the report; directives.max_overrides caps them per change, and directives.require_approval makes them count only once the forge shows an approving review of the head commit by a listed reviewer other than the author. policy_from: base judges a change by the base branch's discipline.toml, so a policy edit takes effect once it has merged.
removes: tests/legacy/ replaced by proptest property suite
allow-assertion-drop: test_allocator single large allocation split into benchmark
allow-ignore: test_darwin_kqueue requires macos runner environment
allow-golden-update: tests/snapshots/v2.snap updated for new serializer format
allow-stub: validate schema lands with the next migration
allow-toolchain-weakening: strict migrating the legacy tree file by file
allow-sandbox-widening: defaultMode the eval harness runs in a throwaway VM with no credentials
allow-agent-instructions: AGENTS.md the new rule was reviewed in #90
allow-build-hook: prepare husky installs the commit hooks
allow-gate-weakening: ci-integrity the discipline step runs on pull_request only; merged-pr-body covers pushes
Commands
Every subcommand of the binary. discipline <command> --help and man discipline list each one's options; CONFIGURATION.md has them as tables.
| Command |
Description |
| discipline check |
Run the configured gates. Exit 0 = pass, 1 = violations, 2 = could not check |
| discipline diff |
Shorthand for checking uncommitted or working tree changes against HEAD |
| discipline baseline |
Record or manage grandfathered finding baselines |
| discipline init |
Write a minimal discipline.toml: gates run at their built-in defaults; commented examples show what to change |
| discipline gates |
List every gate: id, suite, availability, and effective state |
| discipline schema |
Print the JSON Schema for discipline.toml |
| discipline self-test |
Run the embedded negative / positive controls against this binary |
| discipline completions |
Generate shell completion script to stdout (bash, zsh, fish, powershell, elvish) |
| discipline install-hooks |
Install pre-commit hook in the local git repository |
| discipline hook |
Run the gates inside a coding agent's edit loop (Claude Code, Codex, Cursor, Aider, Copilot CLI, agy, Qwen Code, OpenCode) |
| discipline explain |
Explain a gate: what it checks, its state here, and the directive that lifts a finding |
| discipline replay |
Replay the last N merged changes through a configuration: what it would have blocked |
| discipline audit |
List the escape hatches the last N merged changes carried: directives, configuration loosenings, baseline growth, inline markers |
| discipline mcp |
Serve the gates to an MCP client over stdio (read-only tools: check_diff, list_gates, explain_finding) |
| discipline bench |
Benchmark tooling for the bench-regression gate |
| discipline doctor |
Check that the repository and its platform enforce discipline: workflows, CODEOWNERS, branch protection, and the local agent settings. Exit 0 = healthy, 1 = a failing check, 2 = could not check |
| discipline lease |
Claim this worktree's branches for one agent session, so other worktrees' sessions do not move them (kept in the common git directory, never committed) |
Composite Action Inputs
Complete input parameters for GitHub Actions, Gitea Actions, and Forgejo Actions:
| Input |
Default |
Description |
| config |
discipline.toml |
Path to discipline.toml. When the default discipline.toml is absent, built-in defaults apply (each gate at its built-in default enablement and severity); any other path that does not exist is an error (exit 2). |
| suite |
all |
Suite to run: all, agent-guard, hygiene, integrity, quality, verification, bench |
| base_ref |
(none) |
Branch or commit the change is measured against. Default: PR base branch, else the pushed-from commit, else the default branch. |
| enable |
(none) |
Gate ids to force on (comma or newline separated). See discipline gates. |
| disable |
(none) |
Gate ids to force off (comma or newline separated), e.g. "time-estimates, pii". |
| config_override |
(none) |
Inline TOML merged over discipline.toml: tables merge, lists append, scalars replace. |
| hostname_denylist |
(none) |
Hostnames the pii gate must reject (comma or newline separated). Pass a secret; matches are never echoed. |
| fail_on_warnings |
false |
Treat warnings as failures. |
| fail_on_overrides |
false |
Treat the overrides a change writes as failures (requires human sign-off); inline markers already in the tree are not counted. |
| advisory |
false |
Advisory mode: run all checks and emit reports, but exit code 0 even if violations occur. |
| comment |
false |
Post the report as one pull-request comment, edited on every run. Needs pull-requests: write (GitHub) or a token that can comment; a fork's read-only token is reported, not failed. |
| token |
${{ github.token }} |
Forge token handed to discipline: read by the gates that ask the forge (issue-link verify_references, ratified-paths, review-threads, require_approval; GitHub GraphQL needs a token), and used to post the comment when comment is true. |
| policy_from |
head |
Which side's discipline.toml judges the change: 'head' (the change's own copy) or 'base' (the base ref's, so a policy edit takes effect once merged; config-integrity still reports it). |
| actor |
${{ github.event.pull_request.user.login || github.actor }} |
Login judged against allowed_override_actors. Default: the pull request author (github.event.pull_request.user.login), which the server sets; otherwise github.actor or the forge equivalent. The triggering login is not used on a pull request, since whoever edits the description must not be able to authorize their own override. |
| directive_sources |
(none) |
Comma-separated list of allowed directive sources (pr-body, commits, merged-pr-body). merged-pr-body reads, on a push event, the body of the merged pull request each pushed commit arrived through. |
| pr_body |
${{ github.event.pull_request.body }} |
PR description: carries override directives and is itself scanned by hygiene gates. |
| pr_title |
${{ github.event.pull_request.title }} |
PR title: checked by hygiene gates (e.g. issue-link). |
| working_directory |
. |
Directory of the repository to check. |
| version |
(none) |
Release to download (e.g. v0.1.0). Default: the tag this action was referenced by (@vX.Y.Z); for a major tag, a commit SHA or a branch, the release in the action's own Cargo.toml; the latest release only when neither is available. |
| binary_path |
(none) |
Use this discipline binary instead of downloading one (air-gapped Gitea/Forgejo runners, self-tests). |
| download_url |
https://github.com/orieg/discipline/releases |
Base URL of the release store, for mirrors: https://, file://, or http:// to a loopback address. Set DISCIPLINE_INSTALL_ALLOW_INSECURE_HTTP=1 in the environment to accept another cleartext mirror. |
| require_provenance |
false |
Fail the install, before the binary runs, when its build provenance cannot be verified (provenance output unverified). A binary_path binary is accepted. Set it on a mirror install once the mirror serves discipline.intoto.jsonl and the runner has gh. |
| trusted_root |
(none) |
Path on the runner to a Sigstore trusted root (the output of gh attestation trusted-root) for the bundle check of a mirror install, so it needs no network. Never take it from the mirror itself. Default: Sigstore TUF repository, online. |
| install_only |
false |
Install discipline and put it on PATH for the steps that follow, without running a check. For .github/workflows/copilot-setup-steps.yml, so the hooks of Copilot cloud agent find the binary. |
| baseline_file |
(none) |
Path to grandfathering baseline file (defaults to discipline-baseline.toml if present). |
| no_baseline |
false |
Ignore grandfathering baseline even if present. |
| ci_context |
(none) |
Rollup job only: toJson(needs) of the rollup job (inline JSON, or a path to a file holding it) for the ci-skip-set gate. Empty: the gate reports "not evaluated". |
| test_report |
(none) |
Path to test report XML file relative to repo root (JUnit XML) for test-floor identity ratcheting. |
| test_base_report |
(none) |
In-job base test report XML file for test-floor identity ratcheting in dual-report mode. |
| test_head_report |
(none) |
In-job head test report XML file for test-floor identity ratcheting in dual-report mode. |
Composite Action Outputs
Step outputs emitted by the composite action:
| Output |
Description |
| status |
pass, fail, or error |
| errors |
Number of blocking violations |
| warnings |
Number of non-blocking violations |
| failed_gates |
Comma separated ids of the gates that reported a violation |
| overrides |
Number of applied override directives |
| baselined |
Number of grandfathered findings matching baseline (not blocking) |
| overridden_gates |
Comma separated ids of the gates that had an override applied |
| passed_gates |
Number of passing gates |
| examined_items |
Total number of items examined across all enabled gates |
| report |
Path of the JSON report |
| provenance |
How the binary was checked: verified (the build provenance attestation of the discipline release workflow, from the GitHub API or from the Sigstore bundle discipline.intoto.jsonl of a mirror), unverified (SHA256SUMS of the release store only: no gh, or a mirror without the bundle), or local (binary_path) |
| install_error |
Why installing the binary failed (empty on success) |
| bin |
Path to the installed discipline executable |
Architecture & Execution Model
Zero-dependency, standalone binary. With the default gates, a pull-request run reads git in memory and makes no network requests; the forge-reading gates (issue-link verification, review-threads, ratified-paths) and --comment are opt-in.
Installation & Distribution
Available as a one-line installer, native APT/RPM packages, Homebrew formula, pre-compiled static binary, container image, or from source via Cargo.
Install the binary
Quick install Linux & macOS
Download, verify, run. Or the one-liner curl -fsSL https://orieg.github.io/discipline/install.sh | bash.
curl -fsSL -o install.sh https://orieg.github.io/discipline/install.sh \
&& bash install.sh
Homebrew macOS & Linux
Native formula for Apple Silicon, Intel macOS, and Linux.
brew install orieg/tap/discipline
APT Debian / Ubuntu
Signed APT repo for Debian 11+ and Ubuntu 20.04+. Browse repo →
curl -fsSL https://orieg.github.io/discipline/apt/discipline-archive-keyring.gpg \
| sudo tee /usr/share/keyrings/discipline-archive-keyring.gpg >/dev/null \
&& echo "deb [signed-by=/usr/share/keyrings/discipline-archive-keyring.gpg] \
https://orieg.github.io/discipline/apt/ stable main" \
| sudo tee /etc/apt/sources.list.d/discipline.list \
&& sudo apt update && sudo apt install -y discipline
RPM RHEL / Fedora
Signed RPM repo (repository metadata is GPG-verified) for RHEL 8/9/10, Fedora, CentOS, Rocky, Alma. Browse repo →
sudo dnf config-manager --add-repo \
https://orieg.github.io/discipline/rpm/discipline.repo \
&& sudo dnf install -y discipline
MacPorts macOS
A generated Portfile is attached to each release; install it from a local ports tree (see the README).
mkdir -p ~/ports/devel/discipline \
&& curl -fsSL -o ~/ports/devel/discipline/Portfile \
https://github.com/orieg/discipline/releases/latest/download/Portfile
Cargo crates.io
Builds with rustc 1.90+ and a C compiler (vendored libgit2 and the C tree-sitter grammars).
cargo install discipline
Docker container
Non-root Alpine container with static binary.
docker pull ghcr.io/orieg/discipline:latest
Shell completions
The APT, RPM, Homebrew and MacPorts packages install bash, zsh and fish completions, and a release tarball carries them under completions/. For a Cargo install, generate them into your shell's completion directory and regenerate after each upgrade (see the README).
discipline completions zsh > ~/.zfunc/_discipline
Add it to CI and hooks
GitHub Action
Composite action with automatic binary bootstrap. Pinned by commit SHA, which the default ci-integrity gate requires.
uses: orieg/discipline@f3d6d4a0bc4f1f60f63fa101aa27e1a17d0d5276 # v0.32.0
GitLab CI/CD
Job template with spec: inputs (GitLab 16.0+), pinned to a release.
include:
- remote: https://raw.githubusercontent.com/orieg/discipline/v0.32.0/templates/discipline.gitlab-ci.yml
Forgejo Action
Compatible with Forgejo Actions and forgejo-runner.
uses: https://github.com/orieg/discipline@f3d6d4a0bc4f1f60f63fa101aa27e1a17d0d5276 # v0.32.0
Gitea Action
Compatible with self-hosted Gitea and act_runner.
uses: https://github.com/orieg/discipline@f3d6d4a0bc4f1f60f63fa101aa27e1a17d0d5276 # v0.32.0
pre-commit hook
Local git pre-commit hook (discipline / discipline-system).
repo: https://github.com/orieg/discipline
|