A CI quality gate that grades a repo against a product-quality doctrine — not code style.
Project description
Invigil
A CI quality gate that grades a repo against a product-quality doctrine — not code style.
Linters check your code. Dependabot checks your dependencies. Nothing checks whether your project keeps the promises a stranger relies on: that they can boot it in ten minutes, that every error tells them the fix, that the thing on PyPI actually installs today, that the README is still a landing page and not a 600-line wall.
Invigil turns those promises into mechanical, exact-fix-reporting checks and runs them in CI — so the project speaks for itself.
invigilate — to watch over an exam and enforce its rules.
Why
You already wrote the doctrine; you just enforce it by hand. Every failing Invigil check tells you what's wrong, why it matters, and the exact command to fix it — because a gate that can't tell you how to pass it is the same broken-error-message anti-pattern it's meant to catch.
It grades against seven Gates, each a promise to a different stranger:
| Gate | The promise |
|---|---|
| G1 | A stranger succeeds in 10 minutes on a clean machine |
| G2 | Every failure mode tells the user the fix |
| G3 | Published artifacts are machine-verified daily |
| G4 | Supply-chain evidence is public (Scorecard ≥7, signed releases, SBOM) |
| G5 | All five doors open and documented (newbie, operator, contributor, enterprise, AI) |
| G6 | First external contributor merged without hand-holding |
| G7 | Cited/integrated by projects you don't control |
A repo reaches Gn only when every mandatory check for gates ≤ n passes, and gets a letter
grade from its weighted score.
Quick Start
Run it locally on any repo:
pip install invigil
invigil score . # human-readable scorecard + the exact fix for every failure
invigil score . --format markdown # a PR-comment-ready table
invigil score . --format json # machine-readable
Add it to CI as a report-only gate (posts a scorecard comment + badge, never blocks a PR):
# .github/workflows/quality-gate.yml
name: Quality gate
on: [pull_request]
permissions: { contents: read, pull-requests: write }
jobs:
invigil:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: invigil/invigil@v1 # the doctrine scorecard
with:
enforce: "false" # flip to true once the grade is stable
Flip enforce: "true" (or set project.enforce: true in .invigil.yml) when you're ready for
it to block merges below the target gate.
How it works
Two layers, matching the doctrine:
- Static Doctrine Scorecard (every PR, seconds) — inspects the repo filesystem and GitHub
metadata: LICENSE, README length,
.env.example, a deep-health endpoint, a global error-id handler, SHA-pinned actions, an enforced lockfile, a coverage gate, a daily published-artifact smoke test, ≥5 good-first-issues, docs index,llms.txt/AGENTS.md, and more. Emits text / JSON / Markdown / a shields.io badge. - Stranger Gate (nightly, reusable) — on a clean runner, installs and boots each published
artifact you declare and probes its core surface within a 10-minute budget. One reusable
workflow replaces the 60-line
smoke-published.ymlevery repo copy-pastes:
# .github/workflows/stranger-gate.yml
name: Stranger gate
on:
schedule: [{ cron: "0 3 * * *" }]
workflow_dispatch:
jobs:
stranger:
uses: invigil/invigil/.github/workflows/stranger-gate.yml@v1
Configuration
Drop a .invigil.yml at the repo root. It's optional for the static scorecard (sensible
defaults apply) and required for the Stranger Gate (it declares what to boot and probe). Full
schema in schema/invigil.schema.json; examples in
examples/.
version: 1
project:
name: my-app
language: python
min_gate: G4
enforce: false
artifacts:
- type: pypi
name: "my-app[all]"
- type: ghcr
image: ghcr.io/me/my-app:latest
port: 8000
probes:
- { url: "/", expect_status: 200 }
- { url: "/api/things", expect_json_count: { min: 5 } }
boot_budget_minutes: 10
Lightweight & modular
A gate developers bypass is dead weight, so Invigil is built for zero friction:
-
Fast offline groups for pre-commit — each check is tagged
local/network/heavy.invigil check layoutruns the filesystem checks with no network in ~120ms:# .pre-commit-config.yaml - repo: https://github.com/invigil/invigil rev: v1.0.0 hooks: [{ id: invigil-layout }, { id: invigil-secrets }]
Heavier, network-bound checks (
scorecard, the Stranger Gate) stay in CI.invigil score --offline/--layer local/--group supply-chainslice it any way. -
Profiles, so it bends instead of forking.
profile: strict | progressive | light, plus per-checkweights,optional(ding without gating), andthresholds.fail_on. Make it your doctrine, not a hardcoded one. -
Resilient by design. A scorecard.dev timeout is a SKIP that's excluded from the grade — never a false A-to-C downgrade, never a crashed build.
-
AI-era native. The
aigroup checks that yourllms.txt/AGENTS.mdleak no secrets and that agent code declares its tool inventory — the first slice of "what's the blast radius if this agent is prompt-injected?"
The doctrine
Invigil encodes a specific product-quality doctrine (the Silent User Doctrine and its Five Disciplines): absence of complaints is not absence of problems — silence is the loudest negative signal a project gets. You test at release time; users arrive after dependencies drift and registries change. Only automation is awake then. Invigil is that automation.
Contributing
Issues and PRs welcome — see CONTRIBUTING.md and
good first issues. Invigil
grades itself in CI (self-score job); a PR that lowers Invigil's own grade won't merge.
License
Apache-2.0 — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file invigil-1.0.0.tar.gz.
File metadata
- Download URL: invigil-1.0.0.tar.gz
- Upload date:
- Size: 44.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.0.1 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
13883758f7617637a08156d50346e5e0b4146c4040fc4d40fb5fb3eaba992184
|
|
| MD5 |
6904117cb4335d9dd96e30f5666d4115
|
|
| BLAKE2b-256 |
09d23b26b21d89f6590eb91fd46d33f3b5db360a2720f10166198f6cb2b4ace6
|
Provenance
The following attestation bundles were made for invigil-1.0.0.tar.gz:
Publisher:
release.yml on invigil/invigil
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
invigil-1.0.0.tar.gz -
Subject digest:
13883758f7617637a08156d50346e5e0b4146c4040fc4d40fb5fb3eaba992184 - Sigstore transparency entry: 2181689891
- Sigstore integration time:
-
Permalink:
invigil/invigil@7f35b22cf49440276ef18fb31e391f455c607c25 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/invigil
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7f35b22cf49440276ef18fb31e391f455c607c25 -
Trigger Event:
push
-
Statement type:
File details
Details for the file invigil-1.0.0-py3-none-any.whl.
File metadata
- Download URL: invigil-1.0.0-py3-none-any.whl
- Upload date:
- Size: 37.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.0.1 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
652fa4fb334e0c3d5178e902236a815984bcf5aea594ed85b7786a4f48d309e1
|
|
| MD5 |
1ae6c7487d00df3e6b534d85bb2ce83a
|
|
| BLAKE2b-256 |
fac6e94abf0cb6553ab70acf9dd45f83e041978af4850f9ce7b7a81e2a3214c3
|
Provenance
The following attestation bundles were made for invigil-1.0.0-py3-none-any.whl:
Publisher:
release.yml on invigil/invigil
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
invigil-1.0.0-py3-none-any.whl -
Subject digest:
652fa4fb334e0c3d5178e902236a815984bcf5aea594ed85b7786a4f48d309e1 - Sigstore transparency entry: 2181689980
- Sigstore integration time:
-
Permalink:
invigil/invigil@7f35b22cf49440276ef18fb31e391f455c607c25 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/invigil
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7f35b22cf49440276ef18fb31e391f455c607c25 -
Trigger Event:
push
-
Statement type: