Docs

Retrospecting

Ring 2 audits

The four jobs of the Vinaya Archivist workflow that run after merge: what starts each one, what it writes or reports, and which can fail red.

After merge
Workflow
Daily
Drift

About Ring 2 audits

Ring 2 runs on the forge after merge, on events and on a schedule. It catches drift from any writer, including writers the hooks and required checks never saw. It cannot undo a merge; it turns drift into records. See Gates for all three rings.

No role is dispatched while these jobs run. The Principal reads what they find: incident Issues, labelled pull requests, unarchived tranches. The Archivist and Tranche Archivist turns later re-confirm and extend what the jobs did.

How it works

All Ring 2 jobs live in one generated workflow, .github/workflows/vinaya-archivist.yml, named "Vinaya Archivist". Each job checks out with persist-credentials: false, installs with bun install --frozen-lockfile --ignore-scripts, builds the repository's own CLI and runs it.

The workflow starts on:

  • a push to main;
  • an issues closed event;
  • a schedule of 0 2 * * * (daily at 02:00 UTC);
  • workflow_dispatch (a manual run).
JobTriggerReportsFails red
post-merge · archivePush to mainProvenance comment, DANGLING list, Issue closedYes, on a forge error or an unconfirmed close
post-merge · tranche self-archivePush to mainMilestone retrospective section; Milestone closed when freeNo
issue-closedTranche Issue closedSame as tranche self-archiveNo
daily-drift · dead branchesDaily, manualvinaya/dead-branch-push label and comment on the pull requestNo
daily-drift · tranche catch-upDaily, manualArchives finished, unarchived tranchesNo
direct-main-push-detectionPush to main, manualvinaya/direct-main-push incident IssueYes

The jobs

post-merge (Post-Merge Archivist)

Triggerpush to refs/heads/main
Permissionscontents: read, issues: write, pull-requests: write
Step 1vinaya archive --merge-sha=<sha>: provenance comment on the merged task pull request, Issue closed and confirmed. See Task close.
Step 2Resolves the pull request and its head branch; if it is task/<slug>/…, runs vinaya archive tranche <slug> --yes. See Tranche and Milestone close.
ReportsThe provenance block, with any DANGLING entries, on the pull request
Fails redStep 1: yes, on any gh or permission error, or an Issue that does not confirm CLOSED. It skips (exit 0) non-task or already-archived pull requests. Step 2: never (continue-on-error: true).

issue-closed (Tranche Self-Archive)

Triggerissues closed, only when the Issue carries a label containing vinaya/tranche:
Permissionscontents: read, issues: write, pull-requests: read. Builds the default branch only.
DoesRe-reads the Issue's labels; for each vinaya/tranche:<slug> with a safe slug, runs vinaya archive tranche <slug> --yes
ReportsThe archive outcome in the job log; the retrospective section on the Milestone when the tranche is complete
Fails redNever (continue-on-error: true)

daily-drift (Daily Drift Check)

Triggerschedule (daily at 02:00 UTC) or workflow_dispatch
Permissionscontents: read, issues: write, pull-requests: write
Step 1vinaya audit --only=dead-branches
Step 2vinaya archive tranches --yes, the daily tranche catch-up
Fails redNever. Both steps are continue-on-error: true; the workflow calls this job "a notification channel — never fails red".

The dead-branch audit finds work pushed after it was finished:

  1. 1

    List task branches

    It lists the remote task/* branches.

  2. 2

    Keep the finished ones

    For each branch it takes the most recent pull request and keeps only MERGED or CLOSED ones.

  3. 3

    Compare dates

    It compares the committer date of the branch's tip commit with the pull request's merge or close time.

  4. 4

    Report late pushes

    For each branch whose tip is newer, it makes sure the label vinaya/dead-branch-push exists and adds it to the pull request. It creates or updates one comment marked <!-- vinaya/dead-branch-push -->, saying this is a notification, not a merge gate, and that nothing was reverted.

Every command failure is printed as non-fatal; the scan never throws.

direct-main-push-detection (Direct-Main-Push Detection)

Triggerpush to refs/heads/main, or workflow_dispatch
Permissionscontents: read, issues: write, pull-requests: read
Doesvinaya audit --only=direct-push --sha=<sha>
Fails redYes: exit 1 when the commit has no associated merged pull request

It looks up the pull requests associated with the commit and keeps the merged ones. GitHub can link a merge to its pull request with a delay, so it checks up to 6 times, 20 seconds apart, and stops as soon as one appears.

If none appears, it makes sure the label vinaya/direct-main-push exists and opens an Issue titled Direct push to main detected: <first 12 chars of sha>. The Issue is labelled vinaya/direct-main-push, marked <!-- vinaya/direct-main-push -->, and states that it is detection only. An open incident for the same commit is not duplicated.

How these relate to the checks

These jobs are gate mechanisms outside the 37 checks; see Gate mechanisms.

  • The dead-branch-push check refuses a push to a branch whose pull request is closed or merged before it happens, at push and again on the pull request. The daily audit reports the ones that got through.
  • The main-branch-refusal check refuses a commit or a push on the default branch on your machine. Direct-main-push detection is the after-the-fact backstop.

Configuration

KeyEffect
rings.ring2_asyncAuditsfalse skips vinaya archive and the dead-branch audit (with --json they report skipped). It never skips direct-main-push detection, archive tranche or archive tranches. Default true.
managedvinaya init creates the workflow, vinaya upgrade regenerates it, vinaya eject removes it.

The schedule and the jobs are fixed in the generated file; no config key changes them.

What gets logged

ActLog events
Each vinaya archive and vinaya audit runoperation · completed

The findings themselves are forge records (labels, comments, incident Issues), not Log events. vinaya archive and vinaya audit use neither the check runner nor the effect executor, so they emit no gate · checked or effect · verified event. Where a run's Log goes in CI depends on the logs destination; the workflow sets none.

What's next