Docs

Gates

Ring 0 commit checks

The 11 checks the pre-commit hook runs on the files you are committing, on your own machine. Each runs again at push, and again in CI on the pull request.

11 checks
Ring 0
Pre-commit
Diff-scoped

How the commit hook runs

The managed pre-commit hook (.vinaya/hooks/pre-commit) runs three steps in order:

  1. 1

    Format

    bunx biome check --write --staged, then it re-stages the files Biome fixed.

  2. 2

    Typecheck

    bunx turbo typecheck --affected.

  3. 3

    Run the Vinaya checks

    vinaya check --all --diff-only --local --skip-full, with VINAYA_HOST=hook set. This is the gate.

The flags decide which checks run:

FlagEffect
--allEvery resolved check except those declared with ownWorkflow.
--localSkips every check that declares requiresOpenPr (skip reason requires-open-pr, local-only).
--skip-fullSkips every scope: 'full' check (skip reason full-scope, pre-commit). Only the pre-commit hook passes it. It also sets VINAYA_COMMIT_HOOK=1 for the checks it starts, so they may add staged files to their diff.
--diff-onlySkips a scope: 'diff' check with include globs when no changed file matches. None of the 11 checks below declares include, so none is skipped this way.

What is left is every Ring 0 check whose scope is 'diff': exactly these 11.

Where they run again

RingWhereTrigger
Ring 0pre-commit hook, then pre-push hook (vinaya check --all --local, without --skip-full)git commit, git push
Ring 1vinaya-checks.yml, job vinaya check --all --diff-onlypull_request types opened, reopened, edited (not synchronize)
Ring 1The pull-request write path, for the five validates: 'body' checks only: brief-shape, pr-report-density, doc-coverage, doc-coverage-push, pr-premise-reassertvinaya pr create, pr edit, pr report --push, before the body reaches the forge
Ring 2None of these checks—

Checks

brief-shape

A pull request without the brief sections Vinaya requires cannot be reviewed against its brief, so it is refused.

RefusesA brief that is missing or malforms a required section, including Closes #N on a task branch, consumer tests, and an ## Objectives copy that differs from the linked Issue's. On a task branch the graded text is the Issue's frozen aeg:brief:v<k> comment, not the pull-request body.
RunsRing 0: commit and push. Ring 1: CI and the pull-request write path.
ReadsPR_BODY, BRANCH, PR_NUMBER, GITHUB_TOKEN, GH_TOKEN (all optional). On a task branch, the Issue body and comments, read with gh. The workspace package.json files. scope: 'diff', timeout 15 s.
OutcomesExit 0 when PR_BODY is empty, or when a non-task branch's body is not brief-shaped. Otherwise exit 1 with error findings. Findings on a pull request below the configured cutover are printed as informational, not failed. A missing PR_NUMBER counts as not grandfathered.
ImplementsVinaya-specific
SettingsgateCutovers.objectivesSinceIssue, gateCutovers.briefRulesSincePr, gateCutovers.agentBoxesRefusedSincePr; principals (trust anchor, picks the brief's author); checks.brief-shape to replace it; rings.ring1_forgeWriteInterception for the write path only.

pr-report-density

The report's ## Decisions and ## Scope must stay one block each, so the report stays short and readable.

RefusesA ## Decisions or ## Scope section with more than one blank-line-separated block.
RunsRing 0: commit and push. Ring 1: CI and the pull-request write path.
ReadsPR_BODY (optional). Nothing else. scope: 'diff', timeout 15 s.
OutcomesExit 0 when PR_BODY is empty or both sections are one block. Exit 1, one error per over-dense section.
ImplementsVinaya-specific
Settingschecks.pr-report-density; rings.ring1_forgeWriteInterception for the write path.

doc-coverage

A code change bound to a document by .vinaya/doc-owners must update that document or say why not, so the docs never silently fall behind.

RefusesA changed file that matches a .vinaya/doc-owners binding with no edit to the bound doc and no accepted declaration. A Doc-neutral: declaration is checked against the file's real diff.
RunsRing 0: commit (adds staged files) and push (committed diff only). Ring 1: CI and the pull-request write path.
ReadsBASE_SHA (default origin/main, then main), GIT_INDEX_FILE, VINAYA_COMMIT_HOOK, PR_BODY, PR_BODY_FILE, PR_NUMBER, GITHUB_REPOSITORY, GITHUB_TOKEN, GH_TOKEN. The diff <base>...HEAD, plus git diff --cached when VINAYA_COMMIT_HOOK=1. For the waiver, the pull request's labels and the Issue timeline. scope: 'diff', timeout 15 s.
OutcomesExit 0 with no changed files, no .vinaya/doc-owners, or no matching binding. Exit 1 with error findings. The vinaya/waiver:docs label counts only when its last labeller is a principal.
ImplementsEvidence completeness
Settings.vinaya/doc-owners (the bindings); principals (trust anchor, read from the default branch); checks.doc-coverage; rings.ring1_forgeWriteInterception for the write path.

doc-coverage-push

The same documentation rule as doc-coverage, run as a warning so it never blocks a push.

RefusesNothing. Reports the same findings as doc-coverage, as warning.
RunsRing 0: commit and push. Ring 1: CI and the pull-request write path.
ReadsOVERRIDE_DOCS, BASE_SHA, PR_BODY, PR_BODY_FILE, PR_LABELS, WAIVER_LABEL_ACTOR, GITHUB_REPOSITORY, GITHUB_TOKEN, GH_TOKEN. The committed diff <base>...HEAD only (no staged files). scope: 'diff', timeout 15 s.
OutcomesAlways exit 0. Exits early when an override is active: OVERRIDE_DOCS, the vinaya/override:docs label, or the body.
ImplementsEvidence completeness
Settings.vinaya/doc-owners; principals; checks.doc-coverage-push.

pr-premise-reassert

A Premise: pin in a pull-request body states a fact about the repository. If the change makes it false, the pin must fail rather than merge as decoration.

RefusesA Premise: pin in the pull-request body that the current tree no longer satisfies. Pin paths must stay inside the repository after symlinks are resolved.
RunsRing 0: commit and push. Ring 1: CI and the pull-request write path.
ReadsPR_BODY (optional). The files the pins name, from the working tree. scope: 'diff', timeout 15 s.
OutcomesExit 0 when PR_BODY is empty or has no Premise: block. Exit 1 when a pin fails.
ImplementsIntent freshness
Settingschecks.pr-premise-reassert; rings.ring1_forgeWriteInterception for the write path.

single-plan-pr

Two open plan pull requests editing the same tranche plan file race each other, so only one is allowed at a time.

RefusesA diff touching aeg-root/tranches/<slug>.md when another open pull request also touches that same tranche's file.
RunsRing 0: commit and push. Ring 1: CI.
ReadsBASE_SHA (default origin/main, then main), PR_NUMBER, GITHUB_TOKEN, GH_TOKEN. The diff <base>...HEAD. gh pr list --state open --json number,files. scope: 'diff', timeout 15 s.
OutcomesExit 0 when no tranche plan file is touched. A failed gh pr list reads as no other open pull requests (exit 0). Exit 1 on a shared tranche file.
ImplementsVinaya-specific
Settingschecks.single-plan-pr

no-disk-state

Vinaya derives status from the forge, so a file that stores live execution status on disk is refused.

RefusesAny added or modified file directly under aeg-root/tranches/*.md; any newly added .md anywhere under aeg-root/tranches/; any newly added *.tokens.md. A rename counts as an add at the new path.
RunsRing 0: commit and push. Ring 1: CI.
ReadsBASE_SHA (default origin/main, then main...HEAD). git diff --name-status. scope: 'diff', timeout 15 s.
OutcomesExit 0 when nothing matches. Exit 1, one error per offending file.
ImplementsVinaya-specific
Settingschecks.no-disk-state

exec-bits

The runner starts a check script directly, so a script committed without the executable mode cannot start on anyone else's machine.

RefusesA changed file under a checks/bin/ path, or whose first line starts with #!, whose index mode (git ls-files -s) is not 100755. Symlinks (120000) fail.
RunsRing 0: commit and push. Ring 1: CI.
ReadsGIT_INDEX_FILE, VINAYA_COMMIT_HOOK. The committed diff against origin/main (then main), plus staged files at commit. scope: 'diff', timeout 30 s.
OutcomesExit 0 when no diff boundary resolves or nothing offends. Exit 1, one error per offender.
ImplementsContract validity
Settingschecks.exec-bits

ci-shard-coverage

A new CLI test file that is not listed in a CI shard would never run in CI, so the commit that adds it is refused.

RefusesA newly staged apps/cli/**/*.test.ts that is not a line in any staged apps/cli/tests/ci-shards/shard-{1,2,3}.txt.
RunsRing 0: commit (where it does real work) and push. Ring 1: CI.
ReadsGIT_INDEX_FILE, VINAYA_COMMIT_HOOK. Always git diff --cached --diff-filter=A and git show :<shard> (the index). scope: 'diff', timeout 15 s.
OutcomesExit 0 when no new CLI test file is staged. Exit 1, one error per missing file.
ImplementsVinaya-specific
Settingschecks.ci-shard-coverage

changeset-coverage

A change to a published package's shipped files must carry a Changesets entry, so every release names what changed.

RefusesA diff that touches a path inside the package.json files allowlist of a member of a .changeset/config.json fixed group, with no .changeset/*.md (other than README.md) in the same diff. An empty changeset satisfies it.
RunsRing 0: commit and push. Ring 1: CI.
ReadsGIT_INDEX_FILE, VINAYA_COMMIT_HOOK. The committed diff plus staged files at commit, .changeset/config.json, the workspace package.json files, and the current and default branch from local git. scope: 'diff', timeout 15 s.
OutcomesExit 0 on the default branch, on the Changesets release branch, or with an empty diff. Exit 0 with a warning when the diff cannot be determined. Exit 1 with an error on a finding.
ImplementsEvidence completeness
Settings.changeset/config.json fixed; each member's files; checks.changeset-coverage

main-branch-refusal

Work belongs on its own branch and reaches the default branch only by pull request, so a commit or a push on the default branch is refused.

RefusesA commit or a push on the default branch: HEAD is the default branch (refs/remotes/origin/HEAD), unless VINAYA_PUSH_REFS shows a tag-only push.
RunsRing 0: commit and push. Ring 1: CI, where HEAD is detached and it always passes.
ReadsVINAYA_PUSH_REFS (optional, set by the push hook). git symbolic-ref only, no network. The diff, timeout 15 s.
OutcomesExit 1 with an error on the default branch. Exit 0 with a warning when the default branch cannot be determined (it fails open). Exit 0 otherwise.
ImplementsEffect safety
Settingschecks.main-branch-refusal

What gets logged

Every vinaya check run writes gate events:

EventWhenFields
gate · summaryOnce per runran, passed, failed, skipped, failed_checks, duration_ms
gate · checkedOnce per check that did not pass or skipcheck, check_version, policy_version, input_fingerprint, outcome, reason, duration_ms

See Events for every field.

Configuration

KeyEffect on these checks
checksAn entry keyed with a core check name (for example checks.exec-bits) replaces that check, including with a no-op. A namespaced key (owner/name) adds a check. A config that cannot be resolved refuses the whole run.
ringsrings.ring1_forgeWriteInterception: false turns off the pull-request write path for the five validates: 'body' checks. It does not touch the hooks or CI. rings.ring2_asyncAudits does not affect these checks.
prePushprePush.alwaysRun adds test files to the push hook's test run. It does not change which checks run.
gateCutoversUsed by brief-shape.

What's next