About pull request checks
Seven of Vinaya's 37 core checks judge a pull request and nothing earlier. Each one needs the open pull request's body, head or comments, so the local hooks skip it and the forge runs it.
Five implement generic gates (see Gates). Two are Vinaya-specific.
| Check | Implements |
|---|---|
closes-n | Vinaya-specific |
surface-scope | Scope |
test-plan | Evidence completeness |
evidence-fresh | Evidence freshness |
review-gate | Review validity |
principal- | Authorization and Decision validity |
body-bare-digits | Vinaya-specific |
All seven report the same way as every check: exit 0 is a pass, and exit 1 with one or more error lines is a failure.
How they run
All seven belong to Ring 1. Two properties decide where each one actually runs:
- Needs an open pull request (
requiresOpenPr: true).vinaya check -, the form the- all - - local pre-commitandpre-pushhooks use, skips the check. - Own workflow (
ownWorkflow: true).vinaya check -never selects the check. A dedicated workflow runs it by name instead.- all
| Check | Needs open PR | Own workflow | Checks PR writes (validates) | Where it runs in CI |
|---|---|---|---|---|
closes-n | yes | no | body | vinaya- · vinaya check - |
surface-scope | yes | no | — | vinaya- · vinaya check - |
test-plan | yes | no | body | vinaya- · vinaya check - |
evidence-fresh | yes | no | body | vinaya- · vinaya check - |
review-gate | no | yes | — | vinaya- · vinaya review gate; also vinaya- · vinaya review gate (verdict check) |
principal- | yes | yes | body | vinaya- · vinaya check principal- |
body-bare-digits | yes | yes | body | vinaya- · vinaya check body- |
The four workflows:
| Workflow | Trigger | Code it runs |
|---|---|---|
vinaya- | pull_request: opened, reopened, edited (no synchronize) | The pull request's checkout, with the CLI build downloaded from the ci.yml run for the same head |
vinaya- | pull_: opened, reopened, edited | The default branch, built in the job; never the pull request's code |
vinaya- | pull_: opened, synchronize, reopened, labeled, unlabeled | The default branch, built in the job |
vinaya- | issue_comment: created, when the comment contains VERDICT | The default branch, built in the job |
On Vinaya's pull-request writes
A check with validates: 'body' also runs when Vinaya writes a pull-request body, before the body reaches the forge.
vinaya pr create: no pull request number exists yet, so every check that needs an open pull request is skipped.vinaya pr editandvinaya pr report -: the number is passed, and the full- push validates: 'body'set runs.
Checks
closes-n
A pull request must close the task its branch belongs to, so the branch and its Closes #N reference have to agree.
| Refuses | A pull request whose branch and Closes #N reference do not name the same task. |
| Runs | CI: vinaya-, inside check -. Write path: pr edit and pr report --push. Skipped on pr create, where the closesN brief section covers the same rule. Never in the hooks. |
| Reads | PR_BODY; BRANCH (falls back to git rev-); AEG_REPO (falls back to the origin remote); GITHUB_TOKEN or GH_TOKEN. From the forge: the branch's own tranche, and the task Issues that the body's Closes #N references name. Scope diff. |
| Outcomes | Pass. Fail with the gate's message. An unreachable forge leaves no tranche data, and the check then fails with "no topology file found". |
| Implements | Vinaya-specific |
| Settings | checks (replace or no-op). rings. removes it from the write path only. |
surface-scope
A task may only change the files its Issue's ## Surface allows, unless the pull request records the extra paths openly.
| Refuses | A changed path beyond the task Issue's ## Surface (inside an out: glob, or matching no in: glob) that the body's AEG:BEYOND- block does not record. Also refuses, by name, when the scope cannot be determined. |
| Runs | CI: vinaya-, inside check -. Not on the write path. Never in the hooks. |
| Reads | BRANCH; BASE_SHA (default origin/main, then main); AEG_REPO; PR_BODY or PR_BODY_FILE; GITHUB_TOKEN or GH_TOKEN. The changed files from git diff -. The tranche topology from the forge, to find the task's Issue (a backlog branch names its Issue directly), then that Issue's body via gh issue view. |
| Outcomes | Pass. Skip (exit 0) when the branch is not a task branch. Fail naming the first unrecorded beyond-Surface path and the glob it crosses. Fail when the repository cannot be resolved, the forge is unreachable, no Issue number is recorded for the task, the Issue body cannot be fetched, the Issue has no well-formed ## Surface, or its out: line has zero globs. |
| Implements | Scope |
| Settings | checks |
test-plan
Every pull request must say how it was tested, and the Principal's and live items must actually be done.
| Refuses | A body with no ## Test Plan section (or the Test Plan: unit- sentinel). Unticked [principal] items. On a task branch, a [live] line of the task's frozen brief with no passed AEG:LIVE entry at the pull request's head. |
| Runs | CI: vinaya-, inside check -. Write path: pr edit and pr report --push. Never in the hooks. |
| Reads | PR_BODY; BRANCH; PULL_ (the pull request's own head, never the merge ref); GITHUB_ (to read principals, which picks the brief's author); GITHUB_TOKEN or GH_TOKEN. On a task branch, the newest frozen aeg:brief:v<k> comment on the task Issue. Scope diff. |
| Outcomes | Pass. Fail (structural) for a missing Test Plan. Fail as pending for unticked [principal] items. Because the check is marked as owed by the Principal, a run whose only red is this pending failure reads green to check --all's exit code and to the loop's CI reader. Fail closed (never pending) when the frozen brief cannot be read, including a not-found Issue, or when the head has no body. |
| Implements | Evidence completeness |
| Settings | checks; principals (read from the default branch) |
evidence-fresh
The evidence in a pull request body must describe the code actually at its head, not an earlier version.
| Refuses | A body whose AEG:EVIDENCE block does not match the pull request's real head. Group A, the git diff -, is recomputed and compared exactly. Group B, the attested check -, must name the current head on its Head: line, compared by patch id against the real base branch. Group C, the [agent] command headings, must equal the body's own Test Plan command list, in order. Commands are never re-run. |
| Runs | CI: vinaya-, inside check -. Write path: pr edit and pr report --push. Never in the hooks. |
| Reads | PR_BODY; PR_NUMBER; BASE_SHA (else origin/main, then main); GITHUB_TOKEN or GH_TOKEN. Head and base via gh pr view -. Pull request comments when the block is the placeholder. principals from the default branch. Scope diff. |
| Outcomes | Pass. Skip (exit 0) with no body, with no AEG:EVIDENCE anchor (the anchor is opt-in), or with no PR_NUMBER. Fail when the block sits inside <details>, when head and base cannot be resolved, when the merge base cannot be resolved, or when any group mismatches. A placeholder block fails once a Principal-authored aeg:developer:round- marker comment exists; if that read fails, it counts as "a round may have happened". |
| Implements | Evidence freshness |
| Settings | checks; principals (read from the default branch) |
review-gate
Nothing merges without a valid Code review and Security review of the current code, or a Principal's waiver.
| Refuses | Missing, stale, superseded or unauthorized Code and Security verdicts. The gate wants a code-reviewer APPROVE and a security-review PASS bound to the current inputs, or the vinaya/ label applied by a principal. It judges reviews only: it reads no Test Plan state and no other check's result. |
| Runs | CI: vinaya-, job vinaya review gate, from default-branch code. A first step reads comments and labels through the API, and fails without building when there is neither a line-anchored VERDICT: comment nor the waiver label. Re-evaluated by vinaya- when a verdict comment lands (see Gate mechanisms). Not in the hooks, not in check --all. |
| Reads | PR_NUMBER; GITHUB_; GITHUB_TOKEN or GH_TOKEN. Comments, labels, the waiver label's actor, the head (by git ls-remote), base, policy, objectives version and frozen brief hash. Policy and principals come from the default branch. No BASE_SHA or other ref override, by design. Scope full. |
| Outcomes | Pass. Skip (exit 0) with no PR_NUMBER (locally, before a pull request exists). Fail closed when any input cannot be resolved. Fail when the review gate's verdict is fail. |
| Implements | Review validity |
| Settings | checks; principals and reviewPolicy (both read from the default branch) |
principal- test- plan- wait
Merge waits for the Principal: the pull request stays red until every [principal] item is ticked.
| Refuses | Merge while a [principal] Test Plan item is unticked, or, on a task branch, while a frozen-brief [live] line has no passed AEG:LIVE entry at the pull request's head. It grades only this half; a missing Test Plan is test-plan's job. |
| Runs | CI: vinaya-, job vinaya check principal-, from default-branch code. A body edit (ticking a box) re-runs it. Also on the pr edit write path, where its pending-only failure never refuses the write. Not in the hooks, not in check --all. |
| Reads | PR_BODY; BRANCH; PULL_; GITHUB_; GITHUB_TOKEN or GH_TOKEN; the task Issue's frozen brief. Uses the same Test Plan logic as test-plan. Scope diff. |
| Outcomes | Pass. Fail as pending, naming the unticked lines. Fail (blocking, not pending) when the frozen brief cannot be read or the head has no body. Deliberately not marked as owed by the Principal: its red stays red until the box is ticked. |
| Implements | Authorization and Decision validity |
| Settings | checks; principals (read from the default branch) |
body-bare-digits
Numbers in pull request prose belong in code spans, so a bare digit in the narrative text is refused.
| Refuses | A bare digit in pull request narrative prose outside a fenced, indented or inline code span, after the check's built-in exemptions. |
| Runs | CI: vinaya-, job vinaya check body-, from default-branch code. It needs pull_ because its Changesets exemption keys on PR_NUMBER, which a pull_request run's own YAML could redirect. vinaya pr create and vinaya pr edit also run the same logic directly on the body, without the exemption. Not in the hooks, not in check --all. |
| Reads | PR_BODY; PR_NUMBER; GITHUB_; GITHUB_TOKEN or GH_TOKEN. For the exemption: the pull request's branch and author via gh pr view, and releaseActor from the default branch. Scope diff. |
| Outcomes | Pass. Skip (exit 0) for a Changesets release pull request (branch changeset- authored by the release actor) or with no body. Fail once per violation, with its line. |
| Implements | Vinaya-specific |
| Settings | checks; releaseActor (read from the default branch) |
Configuration
| Key | Effect on these checks |
|---|---|
checks | An entry whose key is exactly a check's name replaces it, for example with a no-op from vinaya new noop-. |
rings. | false skips the validates: 'body' pass on the pull-request write path. The CI runs are unaffected. |
principals | Read from the default branch by test-plan, evidence-fresh, review-gate and principal-. |
reviewPolicy | Read from the default branch by review-gate. |
releaseActor | Read from the default branch by body-bare-digits for its exemption. |
What gets logged
Every run through the check runner records one gate · summary event, and one gate · checked event per check that did not pass. A pass or skip records nothing of its own.
gate · checked carries check, check_version, policy_version (planned; written as null), input_ (a hash of the check's environment and changed files, never the values), outcome, a short reason code and duration_ms.
In vinaya- the job sets VINAYA_ and VINAYA_LOG_TOKEN, so events go to the logs.url destination that the default branch's vinaya. declares. With no token (for example on a fork's pull request) nothing is recorded for that run. The other three workflows set neither variable. Nothing is posted to GitHub as a log. See Events for every field.