Skip to content

Release 2.1.199 (2026-08-21)

Bug fixes

  • handle merge-endpoint 'not up to date' rejection, not just the lazy mergeStateStatus precheck (#2857) The auto-update-branch path in wait_and_merge was gated solely on the lazily-computed mergeStateStatus == BEHIND precheck. That field is eventually-consistent, so in a serial batch finalize (#1673) a freshly-behind branch can pass the precheck and the merge endpoint then rejects it with 'the head branch is not up to date with the base branch' — a raw GitHubAPIError that hard-failed the merge stage. Catch that authoritative rejection from github.merge() and feed it into the same update-and-retry path (shared _update_or_abort helper, bounded by the same branch-update cap). Any other merge error re-raises unchanged. Refs #2856.

  • select dev image from asserted primary-language, not filesystem markers (#2859) vrg-validate selects its check pipeline from the asserted [project].primary-language in vergil.toml, but the container-selection tools guessed the language from filesystem markers via detect_language(). The two disagree for a freshly bootstrapped repo whose language markers do not exist yet: a new C++ repo declares primary-language = "cpp" but has no CMakeLists.txt/conanfile, so detection returned "" and handed it the base image. The C++ validate pipeline then ran with no toolchain and failed with a misleading FileNotFoundError: conan.

Introduce one source of truth for language resolution and route every image-selection site through it:

  • config.declared_primary_language() reads the asserted language (mirrors primary_ci_version: None when unset/no config, ConfigError propagates).
  • container.resolve_language() prefers the asserted language and falls back to detect_language() only when nothing is asserted.
  • Route vrg-container-run, vrg-container-cache (build/clean/status), vrg-container-test, vrg-finalize-pr dev-image provisioning, and the .venv mask in workspace_mount_args through resolve_language.

detect_language() remains the filesystem-guess fallback. This lets a new repo be bootstrapped before any language markers exist and keeps the selected image agreed with the validate pipeline.

  • declare require_extra_approval_for_unattributed_changes=False in branch-protection ruleset (#2861) GitHub began emitting a new pull_request ruleset parameter, require_extra_approval_for_unattributed_changes, defaulting it to True server-side. Because desired_branch_protection_ruleset() never declared it, vrg-github-repo-config audit reported phantom drift (actual had the field, expected omitted it) with no local code change — same footprint as the GitHub-injected dismissal_restriction default in #2179/#2180.

Applying with the field declared as False confirmed GitHub honors an explicit value (post-apply audit is compliant), so this is a settable parameter, not a re-injected default. Declaring it False turns the setting off and lets the audit own it, keeping the extra-approval gate from silently re-enabling under our required_approving_review_count=0 flow.

  • bump locked pip to 26.2.1 for PYSEC-2026-3721 (#2865) (#2866) pip-audit flagged pip 26.1.2 (PYSEC-2026-3721 / CVE-2026-13346, fixed in 26.2), failing audit/dependencies on all three Python versions and aborting the 2.1.199 release at merge-release.

The vulnerable pip was pinned in uv.lock, not supplied by the container image - the image ships pip 26.2.1 already. It reaches the tree transitively through the dev group: pip-audit -> pip-api -> pip. No runtime dependency of this project is affected.

uv lock --upgrade-package pip moved pip 26.1.2 -> 26.2.1 and touched nothing else. pip-audit now reports no known vulnerabilities; full vrg-validate is green (5076 tests, 100% coverage).

The advisory concerns doubly-encoded package URLs from a malicious index, materially affecting pip download --only-binary, which this pipeline does not run. Low exploitability here, but the gate is blocking by design and the fix is a lock bump, so there is no case for suppressing it.

Features

  • add 'vrg-vm cp' to copy files/dirs between host and VM (#2855) Adds a bidirectional 'cp' verb to vrg-vm so files (e.g. PDFs for cloud agents) can be moved between the host and the VM a workspace resolves to, which the tooling previously did not expose.

A ':'-prefixed path is on the VM, everything else on the host. All-host paths push (dest defaults to the session workspace dir); a ':'-prefixed source pulls (dest defaults to the host cwd). The destination is always treated as a directory (no rename). Copies are recursive, so directories work.

Transport gains copy_to/copy_from on the protocol and all three backends: limactl copy for Lima, gcloud compute scp --tunnel-through-iap for cloud IAP, and scp for the Azure public-IP path. The command attaches to an already-running box via the same state-reading transport list/status use, and on cloud runs ensure_host_path first so the workspace symlink exists and is writable before the copy. Failures are surfaced with actionable messages (VM missing vs unreachable); a missing local source errors before shelling out.

CI Evidence: All gates passed — full audit bundle available. Download →