Docs

Gates

Ring 1 Issue checks

The five checks that validate a task Issue's title, Objectives, Parts, Surface and Milestone when Vinaya writes the Issue, before it reaches the forge.

5 checks
Ring 1
Issue write
Task grammar

About Issue checks

Five of Vinaya's 37 core checks validate a task Issue's own content. They never apply to a pull request.

All five are Vinaya-specific: they encode Vinaya's task grammar, which is the title, ## Objectives, ## Parts, ## Surface, tranche labels and Milestones.

CheckImplements
issue-title-grammarVinaya-specific
issue-objectives-numberingVinaya-specific
issue-parts-coverageVinaya-specific
issue-surface-globsVinaya-specific
issue-milestone-attachVinaya-specific

Each reports the same way as every check: exit 0 is a pass, and exit 1 with error lines is a failure.

How they run

All five belong to Ring 1, check Issues (validates: 'issue'), have scope full, and run in their own path (ownWorkflow: true). No workflow runs them, and vinaya check --all never selects them. They run in one place: when Vinaya writes a task Issue.

CommandWhat runs the checks
vinaya issue create (and --validate-only)Issue validation, before the create
vinaya issue edit <n> (and --validate-only)The validated edit (or, with --validate-only, Issue validation directly)
vinaya issue objectives editThe validated edit, with a temporary body file

The path applies only to a task Issue: one carrying a vinaya/tranche:* label, or one whose body is task-shaped, which covers backlog Issues. It runs every resolved check with validates: 'issue', core or added by your team, through the same runner vinaya check uses.

The caller passes every fact as an environment variable, so no check re-fetches forge state:

VariableValue
ISSUE_BODYThe body about to be written
ISSUE_LABELSLabels, comma-joined (on edit: the forge's labels plus any on the command line)
ISSUE_TITLEThe --title value, or empty
ISSUE_NUMBERThe Issue number, or empty on create
CURRENT_MILESTONE_TITLEAlways empty on the write path
RESOLVED_MILESTONE_TITLEAlways empty on the write path

The findings join one list with the other Issue gate groups: brief-schema sections, Issue content and rendered-brief shape. The write is refused once if any finding is not a warning. Otherwise the Issue is written with gh. See Gate mechanisms for the full write path.

Checks

issue-title-grammar

Issue titles follow one grammar, so every task reads the same way on the forge.

RefusesA title outside the forge-title grammar: Type: description, Type(scope): description, or [tranche] id — description.
RunsAt Issue write time (create, edit).
ReadsISSUE_TITLE
OutcomesPass. Skip (exit 0) when no title is given, as on an edit that does not change it. Fail with one error per grammar finding.
ImplementsVinaya-specific
Settingschecks

issue-objectives-numbering

Each objective must be a numbered, observable outcome, so Parts can cite it by id.

RefusesA malformed ## Objectives section. Lines must be numbered O<n>. <sentence>, one observable outcome each, with no gaps.
RunsAt Issue write time.
ReadsISSUE_BODY, ISSUE_NUMBER; gateCutovers.objectivesSinceIssue from the working-tree config. On create the number is empty, which the check treats as not exempt.
OutcomesPass. Skip (exit 0) with an empty body. Fail with one error per finding. An Issue numbered below the cutover passes.
ImplementsVinaya-specific
Settingschecks; gateCutovers.objectivesSinceIssue (absent means no cutover: every Issue is graded)

issue-parts-coverage

A Part must point at objectives that exist, so every piece of work traces back to a stated outcome.

RefusesA ## Parts line that cites an objective id the ## Objectives section does not define. Part lines read Part <n> (O<k>[, O<j>]) — <outcome>.
RunsAt Issue write time.
ReadsISSUE_BODY
OutcomesPass. Skip (exit 0) with an empty body. Fail with one error per finding.
ImplementsVinaya-specific
Settingschecks

issue-surface-globs

A Surface glob that matches nothing cannot scope any work, so it is caught before the Issue is written.

RefusesA ## Surface in: glob that matches no tracked file. Globs are expanded with git ls-files; the brief renderer uses the same expansion.
RunsAt Issue write time.
ReadsISSUE_BODY; the repository's tracked files.
OutcomesPass. Skip (exit 0) with an empty body. Fail with one error per unresolved glob.
ImplementsVinaya-specific
Settingschecks

issue-milestone-attach

A task Issue belongs to its tranche's Milestone, and this check names the Milestone when the two drift apart.

RefusesOn edit, a task Issue whose Milestone is not the one its tranche label resolves to (not checked on create).
RunsAt Issue write time, on edit only. On create the attach happens in the same write, so there is nothing to compare yet.
ReadsISSUE_LABELS, CURRENT_MILESTONE_TITLE, RESOLVED_MILESTONE_TITLE
OutcomesPass when the labels are not a task label set, when the resolved title is empty, or when the two titles match. Fail otherwise, naming the Milestone to attach.
ImplementsVinaya-specific
Settingschecks

Configuration

KeyEffect
checksA key equal to a check's name replaces it (for example with a no-op). A namespaced check you add that declares validates: 'issue' runs on the same write path with no further wiring.
gateCutovers.objectivesSinceIssueIssues numbered below it pass issue-objectives-numbering. Read from the working-tree config.
rings.ring1_forgeWriteInterceptionDoes not affect these five. false empties the brief-schema section group on the same write path, but the Issue checks have no opt-out.

What gets logged

Each Issue validation goes through the check runner, so it records one gate · summary event, and one gate · checked event per check that did not pass, to the logs destination. The event carries the check name, outcome, a short reason code and an input fingerprint, never the Issue text. Nothing is written to GitHub as a log. See Events for every field.

What's next