Skip to content

Release 2.1.41 (2026-06-18)

Bug fixes

  • surface audit-stage failure detail and distinguish audit crash from non-compliance (#1693) The vrg-release audit stage reported only 'Repository configuration is non-compliant', discarding the real reason. ReleaseError.detail carried the captured audit output, but progress.run_pipeline built the failure cause from str(exc) (the headline only) and logged just the traceback — so the detail reached neither the terminal nor the full log.

Three fixes:

  • progress.py: write a failing stage's exception detail to the run log, so the summary's 'full log ->' pointer leads to the real reason.
  • vrg-github-repo-config: give the audit a real exit-code contract — 0 compliant, 1 non-compliant, 2 could-not-complete. Config-resolution and GitHub-state-fetch failures (e.g. HTTP 403 reading actions/permissions under an App token) now exit 2 with a clean diagnostic instead of a raw traceback or a false non-compliant verdict.
  • preflight.audit_repo_config: branch on the exit code so a crash is labelled 'audit could not complete', not 'non-compliant', and always carry the captured output as detail.

  • abort vrg-release under agent identity (#1696) vrg-release is documented human-only — branch, tag, merge and finalize are human maintainer actions — but unlike its sibling vrg-submit-pr it had no identity guard. An agent running under the user or audit identity could invoke it and proceed straight into preflight and the release pipeline.

Add the guard at the top of main(), immediately after parse_args and before git.repo_root() or any other side effect, mirroring vrg-submit-pr: if identity_mode.is_agent(), print a release-specific message to stderr and return 1.

Surfaced while debugging #1691, where a user-mode agent run of vrg-release reached the audit stage and hit HTTP 403 on actions/permissions — a wall it should never have reached, because the workflow is not an agent action.

Features

  • add ansible-lint conditional common check (#1695) Add ansible-lint as a sixth conditional common check in validate_common, following the existing run-it-wherever-it-applies model used by the other common linters. A signal-based discovery helper gates the check: it runs only when the repo carries Ansible content (ansible.cfg, .ansible-lint*, galaxy.yml, meta/main.yml, or a playbooks/ or roles/ directory), and is a no-op otherwise - mirroring how hadolint/actionlint skip repos without Dockerfiles/workflows.

The check uses a bundled canonical config (configs/ansible-lint.yaml) pinning the production profile, so every repo lints against the same ruleset rather than per-repo drift. ansible-lint's embedded yamllint is left enabled; the standalone yamllint pass only walks repo-root, .github/, and docs/site/mkdocs.yml, so the practical overlap is limited.

PR CI runs the same vrg-validate driver, so parity is automatic once the dev image ships the binary (tracked in vergil-docker#342). This repo has no Ansible content, so the check stays skipped here.

  • order sessions list by workspace then slot (#1692) Reorder the 'vrg-vm list --sessions' table so workspace is the primary axis: WORKSPACE now renders before SLOT (IDENTITY/WORKSPACE/SLOT/STATE/LAST ACTIVE), and rows sort by workspace first, then slot, with identity as the final tiebreaker. This groups every session for a given workspace together so triaging parallel sessions per repo is a single scan. Refs #1690.