Skip to content

Release 2.1.265 (2026-10-25)

Bug fixes

  • give per-kind advice for deferred publishes (#3561) The deferral comment and the close-finalize message treated every deferred publish as a CD job. A deferred package-index now gets advice to re-run (or wait for) the publish-index workflow in the org's packages repo, resolved from [package].vendor, and to confirm the release's packages appear in the index. CD jobs keep their advice, both are given when both kinds are deferred, and the do-not-resume warning stays (#3543).

  • remove the retired triage/idea/research labels and have the sync delete them (#3564) Drop the three retired intake kind labels from data/labels.json, and make vrg-ensure-label --sync delete them (alongside the registry's delete list) from any repo that still has them. The retired names come from intake_route.RETIRED_LABELS, the single source of truth. Deletion lists the repo's labels first and deletes only those present, so it is idempotent and reports each removal, and a real gh error now propagates instead of being swallowed. Ref #3546

  • audit TypeScript devDependencies, not just production (#3567) The TypeScript AUDIT step drops --omit=dev, so npm audit checks every package in package-lock.json, devDependencies included. A vulnerable docs or test tool now fails the gate. The license summary stays production-only. Refs #3552

  • replace and verify the parent after a cross-repo one-off transfer (#3569) A transfer carries the intake parent across, sometimes only after a delay, so the post-transfer link either failed (Sub issue may only have one parent) or was silently overwritten. The transfer path now links with replaceParent, waits, re-reads the native parent and replaces it again if it reverted, failing loudly with a vrg-epic-move fix after bounded rounds. The recreate path links and verifies once. Ref #3556

  • resolve a bare --epic #N against the current repo's epic home (#3572) vrg-epic-audit --epic parsed a bare #N against the cwd repo, so from a public code repo it looked up the wrong issue. Qualify it with epics.qualify_epic_ref against the current repo, giving /.github#N for a public repo and the repo itself for a self-homed private one. The visibility probe runs under the current repo owner's App token. A full owner/repo#N is used as written, with no lookup. Ref #3415

  • re-poll suspected orphaned check-runs for a grace period before failing (#3574) During a GitHub Actions incident, job results are written back late, which looks identical to an orphaned check-run at the moment of checking. The check waiter (vrg-finalize-pr, vrg-release) and vrg-pr-await declared an orphan after one minute or immediately, advising a close/reopen that discarded results already on their way.

A check pending over a completed run is now a suspected orphan, re-polled (job re-read every 30s) for VRG_ORPHAN_GRACE seconds (default 300) with a warning naming the suspects. A result arriving in the window finishes the wait normally; only an expired suspect raises OrphanedCheckError, naming how long each check waited.

  • retry an empty head-SHA run lookup before failing the evidence gate (#3575) GitHub's run-by-SHA index can trail a just-completed CI run, which failed the vergil-containers 2.1.30 release gate although the run existed. harvest now re-queries an empty lookup with backoff for a bounded grace period (--run-grace-secs, default 300s, inside the step's 15-min timeout), logging each retry, then fails with a message naming the wait. A visible failed or in-progress run still fails at once. Ref #3434

  • stage docs sources like CI before mkdocs runs (#3576) vrg-container-docs now runs vrg-docs-stage and vrg-docs-patch-nav on the host before mkdocs, matching CI's docs staging, so local strict builds pass or fail where CI does. The nav patch goes to a gitignored sibling copy (mkdocs.staged.yml); the tracked mkdocs.yml is never rewritten. The generated paths are added to the managed base gitignore, and the command refuses before writing when any generated path is not ignored (e.g. a committed staged copy). Missing gh auth fails the command; staging is never skipped. Adds git.is_ignored (check-ignore). Ref #3439

  • add vrg-container-cache prune for finished-branch cache images (#3577)

  • fix(container-cache): add vrg-container-cache prune for finished-branch cache images

Cached per-branch images piled up on agent VMs because vrg-finalize-pr only cleans the machine it runs on (#3481). The new prune subcommand keeps an image whose sanitized branch matches a live branch (local, worktree or origin) in any repo of the repo set, removes the rest, and never touches protected branches or images a container uses. Dry run by default; --apply removes and fails loudly on rmi errors. The multi-repo live-branch lookup lives in lib/live_branches.py for reuse by #3566.

  • fix(container-cache): fall back to tracking refs when a repo's origin is unreadable

On a Lima VM the default repo set is the whole mounted projects tree, which includes private repos the agent's App cannot ls-remote, so one unreadable origin aborted every prune. Such a repo now contributes its local refs/remotes/origin/* instead, with a warning naming the repo and the error. Tracking refs are stale only toward listing deleted branches, so the fallback keeps more, never less. Any other git failure still stops the run.

  • test(container-cache): wrap long prune test signatures for lint and ty

  • ensure the in-progress label in a planning item's own repo (#3578) ensure_planning_epic ensured 'in-progress' only in the planning queue home, so a member-repo item moved into planning could not be labelled by epic-create ('in-progress' not found). It now also ensures the label in the target (item) repo, deduplicated when the two coincide. Every entry into planning (the planning sentinel in vrg-epic-move and vrg-issue-create, and vrg-intake-route planning) already passes the item's repo here, so this one change covers all three. Ref #3520

  • record relay submissions on the local worktree (#3579) A branch submitted by name that is already on origin went down the relay path, which opened the PR but never recorded it. When that branch also had a local ready worktree, its state stayed ready with no submission, so the freeze refused pr-watch fix commits and pushes. Record the submission on the local worktree after any successful live open; branches without a local worktree are unchanged. Ref #3525

  • preflight refuses on main/develop divergence with a recovery path (#3580) A fresh vrg-release now fetches main and refuses before anything is cut when origin/main has commits not on origin/develop, naming the commits and the recovery: vrg-release --resume for a release stopped before its back-merge, else a manual back-merge PR merged with a merge commit. Develop ahead of main stays normal; a resume skips the check; a failed fetch or compare refuses. Ref #3527

  • preview the real per-repo verdict in a sweep dry run (#3585) In dry-run mode run_sweep now runs each applicator check-only in a disposable detached probe worktree and reports the verdict a live run would reach (ready, skipped, or a follow-up) instead of reporting every repo ready. The Applicator seam gains a check keyword: the xdist and ci.yml applicators compute the verdict without writing (xdist runs no uv lock), the dependabot target previews with vrg-dependabot-sync --check, and the gitignore target runs --write inside the discarded probe because vrg-gitignore-sync --check is stricter than --write. The xdist sweep also treats a repo that is not a Python repo as out of scope, so it no longer files follow-up issues against docs or container repos. Ref #3549

  • file a fix task when an updater or hook fails (#3586) An update-kind failure (an updater or the repo hook failing before validation) opened no PR and filed no fix task, so it was recorded only in run output and the release tracking comment. fix_task_stage now files a fix task for every failure. For an update failure the task stands on its own: it names the failing updater (recorded on DepsFailure.updater), carries the error and output, and says how to reproduce it on develop and where to cut the fix branch. failure_summary names the fix task for update failures too, and failure_markdown no longer tells the reader to reproduce on a branch that was never pushed. Docs updated to match. Ref #3550

  • scan the C++ conan audit in the lock's Debug configuration (#3587) conan audit scan already covers test_requires and tool_requires (every non-root graph node, both contexts) and implicitly loads conan.lock, but it resolved the default Release profile, so locked packages a recipe requires only in Debug (the build_type conan lock create and INSTALL use) were never scanned. Pin -s build_type=Debug and pass --lockfile=conan.lock explicitly on both AUDIT conan steps so the scan matches the lock and a missing lock fails instead of re-resolving. Verified against Conan 2.33 with a test recipe.

  • give the human exact commands to resolve a submitted PR's merge conflict (#3589) A conflict on an already-submitted PR can only be resolved by the human: the branch is frozen to agents. vrg-finalize-pr now replaces the generic advice with the worktree path (or how to create one), fetch, merge of the base branch (not a rebase, since the PR is public), validation, raw-git commit and push, and the re-run. pr_merge raises a MergeConflictError subclass so other callers are unaffected. Ref #3555.

  • defer a package-index miss before close-finalize so the tracking issue stays open (#3596) Move the package-index stage ahead of close-finalize so an index-only timeout reaches the deferral comment and leaves the tracking issue open, as a CD publish deferral does. A stage that records its own failure as a publish deferral is no longer added to the halted-stage list, so close-finalize does not mistake it for a resumable failure. The publish-status and consumer-refresh hold messages now use publish_deferred_remedy for per-kind advice. Ref #3557.

  • comment on the open fix task instead of filing a duplicate on a re-run (#3597) Before filing a fix task, look for an open one with the same version-keyed title and the same failure kind (its '## Failure: ' heading). If one exists, add the new failure to it as a comment and link that task from the PR body and run summary. A failed or malformed lookup fails loud rather than risk a duplicate. Ref #3560.

  • run the image's mkdocs when a Python repo has no docs dependency group (#3598) vrg-container-docs ran 'uv sync --group docs' for every repo with a pyproject.toml, so a repo with no docs group (vergil-tooling itself) failed with uv's 'Group docs is not defined'. CI's docs jobs sync no group: they run 'uv run mkdocs', which falls through to the docs image's mkdocs-material. Match that: sync the docs group only when pyproject.toml declares one, otherwise run plain 'uv run mkdocs' and print a note saying the image's mkdocs is used. An unparseable pyproject.toml now fails with a clear error before the container starts.

  • keep other repos' branch images in finalize's prune; report rmi failures (#3599) vrg-finalize-pr's orphan-image sweep now asks the platform repo set (the same live-branch lookup and plan_prune verdicts as vrg-container-cache prune) instead of the finalized repo's local branches, so it no longer deletes other repos' in-progress images. It is skipped, with a message, where no repo set can be confirmed (the physical host), and a failed lookup or rmi is a warning, never a failed finalize. clean_branch_images and the sweep move to lib/cache_prune.py and return their rmi failures; vrg-container-cache clean and clean-all name each failed image with the runtime's error and exit 1. Ref #3566

  • make vrg-gitignore-sync --check and --write agree (#3600)

  • fix(gitignore): make vrg-gitignore-sync --check drift exactly when --write would change the file

--write now removes exact repeats of the fence's own patterns outside the fence (the fence is the single source of truth), and --check now flags a fence that is not first, a missing trailing newline, and any other layout --write would rewrite. --write then --check always passes. Ref #3568.

  • fix(fleet-sync): preview the gitignore target with vrg-gitignore-sync --check

With --check and --write consistent, the gitignore dry run no longer needs to write inside the probe worktree; drop check_predicts_write. Ref #3568.

  • self-heal a conflicting release PR by merging main into it (#3601) When the release PR is CONFLICTING, merge-release now merges origin/main into the release branch in its worktree and pushes it, then waits again (bounded to 3 attempts). A conflict in CHANGELOG.md alone is resolved by keeping the release branch's generated changelog; any other conflicted path aborts the merge and stops with the exact commands to resolve it by hand. Re-runs are idempotent: an already-merged main is not merged again, and a local merge a prior run did not push is pushed. Refs #3526.

  • say whether close-finalize closed the tracking issue or left it open (#3609) The close-finalize completion comment always read 'Tracking issue closed. Repository finalized.', even when a deferred publish (CD jobs or package-index) or a halted earlier stage left the issue open. It now mirrors close_and_finalize's branches and names each deferred kind. Ref #3593

  • skip gitignored files in the common lint checks (#3610) vrg-container-docs build copies the gitignored release notes and changelog into docs/site/docs/, and the common markdownlint check then linted them, failing the next vrg-validate until they were deleted by hand. Every file list the common checks discover (markdownlint, shellcheck, yamllint, hadolint) now drops the paths the repo's .gitignore excludes, via a new batched git.ignored_paths (one git check-ignore --stdin call). Tracked files are always kept, and a check-ignore failure raises rather than linting less. Ref #3594

  • self-home a private repo whose owner has no .github (#3614) resolve_epic_home crashed with a traceback for a personal-account repo with no /.github: the .github visibility probe failed with 'Could not resolve to a Repository'. A private repo whose owner has no .github now homes its epics in itself (there is no central home). Only GitHub's not-found answer counts as absent; every other probe failure raises EpicHomeError, which cli_main prints as a one-line ': ' instead of a traceback. A missing token keeps its own error. The not-found classification is factored out of repo_exists as github.is_repo_not_found. Docs record the new row of the epic-home rule.

  • fail validation cleanly when the image rebuild fails (#3616)

  • fix(finalize-pr): report a failed cache re-provision as a warning instead of a stage failure

When validation's container fails to launch, finalize rebuilds the dev image and retries. A failed rebuild (the low-disk guard refusing it, a failed cache build or commit, or an unresolvable version window) now prints a warning naming the image, the full error text and the retry command, marks the validation stage warn, and skips the retry, instead of failing the stage with a raw exception after the merge has landed. Ref #3605

  • fix(finalize-pr): fail validation when the dev-image rebuild fails

An unrun validation is an unknown result, not a pass (#3316). A failed rebuild now raises FinalizeError naming the image, the full original error text and the retry command, so the validation stage is recorded failed and vrg-finalize-pr exits non-zero, while the CD check and final summary still run. The pointless second validation attempt is still skipped. Ref #3605

  • guard branch creation through vrg-git branch (#3617) vrg-git branch , and its -c/-C copy and -m/-M rename forms, now apply the same issue-branch name-format check and open-task guard as worktree add -b, checkout -b and switch -c. Listing, deleting, upstream and description forms pass through unchanged. Renames are covered because renaming to an issue-branch name creates that branch as surely as creating it; renames to non-issue names are unaffected. Ref #3606

  • fail org-wide vrg-queue and vrg-epic-audit clearly when /.github is missing (#3618) Add epics.require_org_home, which probes /.github with github.repo_exists before the org-wide paths of vrg-queue and vrg-epic-audit use it. A missing .github raises a one-line EpicHomeError saying there is no org-wide queue or epic home and pointing at --repo; any other probe failure is an EpicHomeError carrying the API error, and a missing token keeps its own message. Ref #3613.

Documentation

  • document the report-only Dependabot posture and the per-org automation Apps (#3521) Replace the vergil-release[bot] identity with the org-owned -automation Apps (Administration read-only, Dependabot alerts read). Document the security-settings half of the config audit, where vrg-dependabot-audit runs, and the dependabot.yml scaffolding. Ref #3423.

  • add the security-boundary register for cross-system seams (#3570) Add docs/site/docs/guides/security-boundaries.md with three entries: coarse GitHub App permissions (open), repo-supplied root provisioning in a credentialed VM (resolved by vergil-vm#105), and the misfiring protected-branch hook that pushes agents to disable the sandbox (open, vergil-claude-plugin#694). Add it to the nav and link it from the permission-model and identity-architecture guides.

  • document vrg-container-cache prune in the CLI tools overview (#3611) Add the prune subcommand to the vrg-container-cache entry: what it keeps, dry run by default, --apply and --root, the platform default repo set, the local-ref fallback when an origin cannot be read, and that vrg-finalize-pr runs the same sweep on VMs and skips it on the physical host. Refresh the attribute table to match the CLI. Ref #3595

Features

  • allow pr update-branch for the USER identity (#3559) gh pr update-branch [--rebase] updates a PR branch onto its base server-side, with no checkout or force-push. Add it to the user/human pr allowlist; it stays off the audit allowlist because it writes. Tests cover both identities, and the permission-model guide lists it. Ref #3542

  • redirect agents to vrg-pr-workflow report-ready from --help (#3562) Run the agent identity gate before argument parsing so an agent's --help reaches it and prints a human-only redirect to vrg-pr-workflow report-ready instead of the usage. The refusal message for other agent invocations now names the same handoff. Human --help is unchanged. Ref #3545

  • add index --check-config to validate packages.toml without building (#3565) Loads packages.toml through the index config parser and exits non-zero with the PackageError message on any invalid value, using no network, keys or output directory. --keys and --out stay required for a real build, enforced with an argparse error. Ref #3551.

  • allow run rerun and read-only workflow/cache commands (#3573) Allow run rerun for the user agent so it can retry a failed CI job. Allow workflow list/view and cache list for every identity, audit included, since they only read CI configuration and state. Deny workflow run for every identity with an explanatory refusal: dispatching a workflow can start CD or a release, which is a human action. Bring the vrg-gh allowlist and deny tables in the permission-model guide into line with the code. Ref #3420

  • stream the publish-index run in the package-index stage (#3581) The package-index stage now finds the earliest non-cancelled Publish package index run in /packages created since the CD release job started, streams it with watch_workflow, and follows the next run when one is cancelled. Whenever no run is streamed it prints a heartbeat per poll. Run-following is progress output only: a gh failure there falls back to polling, and the index poll remains the sole pass/fail check under the single deadline. Ref #3528

  • add day x repo summary, per-day pages and --repo filter (#3582) vrg-activity-log gains --out-dir, which writes index.md (a day x repo table of closed-issue counts), days/.md (that day's issues grouped by repo) and repos/.md (each repo's flat ledger), and --repo, which prints one repo's flat ledger. The default stdout output is unchanged so the nightly docs refresh keeps working. The search limit rises from 100 to gh's 1000 cap, with a stderr warning when it is hit, since a truncated window would leave the summary's older rows silently empty. Ref #3544.

  • add --all-versions to run the coverage gate on every CI version (#3583) vrg-validate --all-versions (Python only) adds one test stage per version vrg-ci-resolve resolves, after the test stage. Each stage has uv install the version as a managed interpreter, syncs the dev group into its own environment under the temp dir, and runs the unchanged pytest --cov-branch --cov-fail-under=100 gate with its reports redirected beside that environment. The container interpreter's own version is skipped, as the plain test stage covers it. Non-Python languages and --check values other than test are refused. The default vrg-validate is unchanged. Ref #3547

  • report undeclared ruleset parameters as a distinct audit category (#3584) A live ruleset rule parameter that the desired rule of the same type does not declare, and that is not strip-listed, is now reported as an UndeclaredParam naming the ruleset, rule type, key and live value, with guidance to declare it on/off in the spec or add it to _STRIP_RULE_PARAMS. Undeclared keys are removed from the value comparison so they no longer surface as opaque .rules drift. They keep the audit NON-COMPLIANT; apply still converges value drift but cannot resolve them (GitHub re-applies its default) and exits 1.

  • add an OSV-Scanner vulnerability audit to the Java profile (#3588) The Java AUDIT stage only resolved the dependency tree and checked licenses; nothing compared dependencies against an advisory database. It now writes a CycloneDX SBOM of the whole reactor (every module and scope, test included) with the cyclonedx-maven-plugin, then scans it with osv-scanner, failing on any known vulnerability. A repo-root osv-scanner.toml holds reviewed suppressions, and a dev image without osv-scanner fails with a message naming the fix instead of skipping. Ref #3554

  • refuse epic/archive-labelled issues where a task is expected (#3591) Add epics.non_task_kind / non_task_ref_kind / non_task_refusal_message, a shared label-based test and refusal text for an epic or archive given where a task is expected. report-ready and vrg-submit-pr now refuse archives as well as epics through it, and vrg-epic-link --task and vrg-issue-create --blocked-by gain the same guard (fail closed on a failed label read). Ref #2842

  • auto-prune per-branch cache images when the image store runs low (#3607) Before a cached dev image is built, read the free space of the image store's filesystem. Below VRG_CACHE_PRUNE_FREE_GB (default 10 GB, 0 disables) run the same prune as vrg-container-cache prune --apply against the same repo set, printing each removed image. A host with no repo set is not guessed; prune failures and refused rmi are warnings. The build then continues only with at least 3 GB (or the threshold, if lower) free, otherwise it stops with the numbers and the manual command. Ref #3571

  • refuse epic, archive and closed issues when creating an issue branch (#3608) vrg-git worktree add -b, checkout -b and switch -c now read issue (state and labels in one gh call) for a /- branch and refuse it when the issue is closed or labelled epic/archive, using the shared epics.non_task_refusal_message. A failed or malformed read refuses with an actionable message. Adds epics.non_task_kind_of_labels so the label check is reused without a second lookup. Ref #3592

  • report-ready pushes the branch itself, not just the relay ref (#3612) report-ready now pushes the reported commit to origin/ (setting upstream) before recording anything, so callers no longer need a separate push. A GitHub workflow-permission refusal is reported as expected and the state is still recorded; any other push failure fails with no local state and no relay ref. A re-run on a frozen branch whose HEAD drifted is refused before pushing. The workflow-refusal matcher moves to lib.git so vrg-git and report-ready share it. Ref #3602

  • prune finished cache images inside the VM on vrg-vm update (#3615) After the reinstall, vrg-vm update (and each box of --all) runs the box's own vrg-container-cache prune --apply over the existing transport. Lima boxes get their identity's projects_dir as --root because the in-guest default needs a checkout cwd; off-platform boxes use their default /vergil/projects. A probe tells a missing or pre-prune vrg-container-cache apart from a real failure and skips with a warning. A failed prune prints its output and exit loudly but never fails the update. --no-prune opts out. Ref #3604

Refactoring

  • one RETIRED_LABELS in lib/labels.py; repo_init reuses the label sync (#3590) Move RETIRED_LABELS, labels_to_delete and the whole-registry sync into lib/labels.py (sync_labels). vrg-ensure-label --sync, intake_route and the epic-audit orphan pass import from there; epic_audit loses its private copy. repo_init's label step now calls the same sync_labels, so a newly initialised repo also gets deprecated and retired labels deleted. Ref #3558.

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