Rollout Phase B — Tag + Registry + Bundled Consumer PR

SkillCommunication

Phase B of a qvac-fabric rollout — tag the merged fabric commit, publish to registry, land the consumer side (vcpkg bumps + package versions + changelogs for all 7 consumers) as one PR, post one Slack message. Pass --bump-only to resume a part-done rollout and do just the version + changelog work. Pass --on-top-of-pr to put the whole consumer side onto an open PR — revert the overlay commit Phase A placed there AND land the bumps on that same PR, instead of opening a separate bundled PR.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Rollout Phase B — Tag + Registry + Bundled Consumer PR skill

What this skill tells your AI

The instructions your AI receives, as published by tetherto/qvac in packages/ocr-ggml/.agent/skills/rollout-phase-b/SKILL.md and read by ahel’s review.

Prerequisites: Fabric PR merged. Phase A all green. v<VERSION> tag does NOT exist yet.

The 7 consumers: embed-llamacpp, fabric, llm-llamacpp, model-fit, ocr-ggml, translation-nmtcpp, vla-ggml.

classification-ggml is not a fabric consumer — it dropped the qvac-fabric vcpkg dependency and now consumes the published npm package @qvac/fabric. It needs neither overlay validation nor a version>= bump during a rollout. Do not re-add it.

THE GOLDEN RULE

Never bump default-registry.baseline in vcpkg-configuration.json. Anywhere. Ever.


Step 0 — Preflight, then ask

Do this before anything else, in every mode. Nothing in B1 or B2 runs until the user approves.

Where the fabric details come from

CaseBehaviour
Given on the invocationUse them verbatim, for the whole run.
Not givenDerive from context (ladder below), recording the source of each value.
NeitherName what is missing and stop.

What was supplied is what gets used. Do not substitute the registry's latest published version, the consumers' version>= floors, or the newest fabric tag for a value the user gave you, and do not "correct" a supplied value to one of them. Remote resolution below annotates the values; it never replaces them.

The version is passed bare10069.0.0, not v10069.0.0. git tag v<VERSION> and the registry portfile's REF v${VERSION} both add the v. A v-prefixed value is malformed: report the expected form and ask, rather than silently stripping it.

Derivation ladder — only for values that were NOT given

Take the first that yields a value, and remember which one it was:

  1. This session — a /rollout-phase-a run, or a version stated explicitly.
  2. The overlay port's own versionvcpkg-overlays/ports/qvac-fabric/vcpkg.json "version", on the Phase A validation branch or (in --on-top-of-pr mode) on the target PR's head branch. Phase A Step 2 sets this to the exact target. Best source when either branch is to hand.
  3. --bump-only only: the 7 consumers' vcpkg.json "version>=", which must all agree. In this mode the floors are the target — that is the mode's precondition.
  4. <merge-commit-sha>: the HEAD of the release branch temp-<N> (see below), or the fabric PR's merge commit.

Never derive a NEW version from the registry's latest entry, the newest v* tag, or the current version>= floors. All three hold the previous version, so a value taken from them re-tags an already-tagged commit and republishes a live one. --bump-only is the single exception, because there the previous version is the target.

The release line is temp-<N>, not master

Fabric releases are cut from branches named temp-<upstream-llama.cpp-build>, and the version is <build>.<major>.<minor>. master tracks upstream and is not the release line — comparing a release commit against it reports diverged and tells you nothing.

VersionRelease branchTag sits at
10069.0.0temp-10069branch HEAD (63bfbdea0)
9341.1.6temp-9341branch HEAD (55325c0c8)

So <merge-commit-sha> should be on temp-<N> for the <N> leading the version you are publishing. A commit that is not is the strongest available signal that the version and the commit disagree.

Resolve against the remote — read-only

tetherto/qvac-fabric-llm.cpp is public; no token needed.

FactCommand
does v<VERSION> already existgit ls-remote --tags https://github.com/tetherto/qvac-fabric-llm.cpp "v<VERSION>"
release-branch HEADgit ls-remote https://github.com/tetherto/qvac-fabric-llm.cpp "refs/heads/temp-<N>"
commit subject + dategh api repos/tetherto/qvac-fabric-llm.cpp/commits/<sha> --jq '{sha:.sha,msg:.commit.message,date:.commit.committer.date}'
is the commit on temp-<N>gh api repos/tetherto/qvac-fabric-llm.cpp/compare/temp-<N>...<sha> --jq .statusidentical or behind means yes

Expected tag state — flag it, never resolve it yourself:

  • full run → v<VERSION> must NOT exist; if it does, proceeding re-tags a published version
  • --bump-onlyv<VERSION> must exist; if it does not, the registry PR probably has not merged

Where the overlay is, and the safety net

--on-top-of-pr <pr-url> says Phase A put its overlay commit on an open PR rather than a dedicated validation branch. Step 5b reverts it — and B2's bumps land on that same PR, not on a new one. See the mode table at the head of B2; the flag governs the whole consumer side, not just the revert.

If the flag is absent, still look. Run the completeness sweep in Step 5b against the working repo before proceeding. If it finds overlay artefacts, stop — name exactly what was found and which branch or PR carries it, and say --on-top-of-pr is required:

/rollout-phase-b: refusing to bump over a live overlay.

  ! vcpkg-overlays/ports/qvac-fabric/       present on <branch>
  ! "overlay-ports" key in 7 packages/*/vcpkg-configuration.json

An overlay makes vcpkg resolve qvac-fabric from the local portfile instead of the
registry, silently. Bumping the floors now ships a version that was never validated
against the published port.

Re-run with --on-top-of-pr <pr-url>, or remove the overlay first.

This is the same invariant --bump-only preflight check 6 enforces; the flag is what tells the skill where to go and fix it rather than only complain.

Present, then ask

Print every value with its provenance, then ask. The label is what lets the user catch a bad derivation — it is the reason deriving is safe at all, so never omit it:

Phase B — confirm before proceeding:

  fabric version   10069.0.0    (as given)
  tag to create    v10069.0.0   ⚠ already exists — proceeding re-tags a published version
  release branch   temp-10069
  merge commit     63bfbdea0    (derived: temp-10069 HEAD)
                   "Merge pull request #204 …"   2026-08-06
  mode             full (B1 + B2) — bumps land on a new branch off origin/main

The mode row must name where the bumps will land, so a wrong target is caught before anything is pushed. In --on-top-of-pr mode it reads:

  mode             full (B1 + B2) — bumps land on PR #3725, no separate PR

--bump-only shows the version, that v<VERSION> exists, and that the registry has published it — no commit row.

--on-top-of-pr adds two rows naming what Step 5b will revert, resolved before the prompt so the user can catch a wrong PR or a missing overlay commit while it is still cheap:

  overlay on       PR #3352  head QVAC-21981/abot-world @ a2d3265d
  reverting        5a950ade2  "… overlay-validate the 7 fabric consumers against …"

Then AskUserQuestion with exactly:

Proceed · Correct a value (say which) · Abort

When a is present, Proceed's description must name what is being accepted. The run does not start until Proceed is chosen — silence is not consent, and approval is required even when every value resolves cleanly.

If nothing yields a version

Name the missing value and stop. Never guess a version, never default it, never fall back to the newest tag:

/rollout-phase-b: no fabric version supplied, and none could be derived.
usage: /rollout-phase-b <fabric-version> <merge-commit-sha> [--bump-only]
   eg: /rollout-phase-b 10069.0.0 63bfbdea0
Nothing was changed. Re-run with the version you intend to publish.

--bump-only (resume mode)

For the common resumed state: the fabric tag is pushed, the registry PR merged, and the vcpkg.json floors already bumped — all that is left is the mechanical version + changelog work.

Skips B1 entirely, and skips Step 7 sub-steps 1–3. Performs only Step 7 sub-step 4 (package.json) and sub-step 5 (CHANGELOG.md), for all 7 consumers. Everything the mode skips, it first verifies was actually done.

Combining it with --on-top-of-pr is legitimate — the overlay outlives a part-done rollout as easily as a whole one. In that combination Step 5b still runs, before the bumps, and preflight check 6 is what it satisfies. The bumps land on the target PR here too, and checks 2 and 7 below need the merge-base treatment (Step 5b's $BASE) rather than origin/main, since the target branch is not cut fresh from main.

Preflight — verify, then stop or proceed

Step 0 runs first. These checks compare against the version the user confirmed there; if Step 0 never reached approval, none of this runs. --bump-only does not make the version optional — the mode skips B1's work, not its input, though it does widen how the version may be derived (Step 0's ladder item 3).

Run all checks and report every failure in one list. Do not stop at the first — a part-done rollout usually has more than one gap, and drip-feeding them wastes a round trip each.

#CheckHow
1Working repo is the real code repo on the right remotegit -C <repo> remote -vtetherto/qvac
2On a branch off origin/main — not main, not release-* (in --on-top-of-pr mode: on the target PR's head branch)git -C <repo> rev-parse --abbrev-ref HEAD
3Fabric tag v<VERSION> existsgit -C <fabric-repo> ls-remote --tags <remote> "v<VERSION>"
4qvac-fabric <VERSION> is published to the registrysee command below — non-null result means published
5All 7 vcpkg.json "version>=" already equal <VERSION>read each packages/<consumer>/vcpkg.json
6Overlay fully removedthe completeness sweep in Step 5b, repo-wide — not just the 7
7No default-registry.baseline driftgit -C <repo> diff $BASE -- "packages/*/vcpkg-configuration.json" → empty ($BASE = origin/main, or the merge base in --on-top-of-pr mode)
8Per consumer: origin/main package.json version vs npmnpm view @qvac/<pkg> dist-tags.latest

Check 4 — request the raw blob so no base64 decoding is needed:

gh api repos/tetherto/qvac-registry-vcpkg/contents/versions/q-/qvac-fabric.json \
  -H "Accept: application/vnd.github.raw" --jq '[.versions[].version] | index("<VERSION>")'

A number means published; null means the registry PR has not merged — stop.

Check 5 — parse the JSON; do not grep a fixed line offset from "name": "qvac-fabric". The dependency object's shape varies between consumers — three of the seven carry extra keys, so a grep -A1 reports the floor as missing when it is present and correct:

ConsumerExtra keys on the qvac-fabric dependency
ocr-ggmldefault-features, features
translation-nmtcppdefault-features, features
vla-ggmlfeatures

Read the version>= field out of the parsed object.

Check 6 matters more than it looks. A leftover overlay makes vcpkg resolve qvac-fabric from the overlay instead of the registry — silently. Nothing errors; you would ship a bump that was never validated against the published port.

Run Step 5b's sweep for it rather than checking only the 7 consumers — an overlay added by hand outside the roster resolves just as silently. On failure, name the branch or PR still carrying it and point at --on-top-of-pr; a bare "check 6 failed" leaves the user to find it themselves.

Check 8 has four outcomes, not pass/fail:

OutcomeMeaningDo
npm == origin/mainnormal — the bump is genuinely pendingproceed
npm behind origin/maina bump already landed and is unreleasedstop and ask before adding another (/bump-version Step 1's rule)
npm ahead, major differsthe package's line was renumbered — see belowproceed on the origin/main number
npm ahead, same linewrong checkout or wrong branchstop

Two cases that look like failures and are not:

  • A package reading 0.0.0 on npm with no release tag has never been published — 0.0.0 is npm's name placeholder, not a real release, so it presents as the behind case. Do not paper over it; ask whether the version already on origin/main should publish as-is before bumping past it. (model-fit was the standing instance and has since published — re-derive rather than assume; npm view @qvac/<pkg> dist-tags.latest plus git ls-remote --tags origin "<prefix>-v*".)
  • @qvac/translation-nmtcpp reads 10.0.0 on npm against 0.8.0 on origin/main: the renumbered case, not a bad checkout. Its main line was restarted at 0.x (there is a live release-0.7 dist-tag at 0.7.0), so npm's latest will stay numerically ahead indefinitely. origin/main is the authority for the bump. Confirm the branch is right, then proceed from 0.8.0.

Step 7 sub-step 3 (Phase A C++ fixes) cannot be verified mechanically. Do not claim a check you did not perform. Instead print the non-manifest diff and have the user confirm:

git -C <repo> diff --stat $BASE -- "packages/*/addon" "packages/*/src"

($BASE = origin/main, or the merge base in --on-top-of-pr mode — on a target PR branch origin/main folds in the author's own C++ and makes this unreadable.)

On success — apply the bumps

/bump-version is the single source of truth for bump level and changelog shape. Note that it is disable-model-invocation: true, so it cannot be invoked programmatically — follow its written Steps 3–5 procedure here. (Alternative, if you prefer the skill itself: run /bump-version <pkg> per package by hand, using /bump-version translation-nmtcpp major for the one post-1.0 package.)

  • Bump level comes from the table in Step 7 sub-step 4 below: 0.x → minor, >=1.0 → major. /bump-version never auto-selects major, so any package on >=1.0 must be given the level explicitly. As of 2026-08-13 no consumer is — all seven are 0.x, translation-nmtcpp included. Do not pass major for it out of habit; see Step 7 sub-step 4.
  • Changelog entries use the fabric-only wording in Step 7 sub-step 5, with the bracketed ## [<version>] - <date> heading.
  • model-fit's CHANGELOG has a ## [Unreleased] block — insert the dated entry below it, per /bump-version Step 5. Inserting above makes the release extractor slice the body off.

Stop point

Apply the 14 edits (7 × package.json + 7 × CHANGELOG.md), then stop and confirm before committing. Print:

  1. a summary table — pkg · old → new · level;
  2. git -C <repo> diff --stat $BASE — or, in --on-top-of-pr mode, git diff --cached --stat, since the target branch carries the author's work and can never reduce to the 14 files.

Assert the diff touches only package.json and CHANGELOG.md across the 7 packages — anything else means something got swept in. Then ask the user to confirm before continuing into Step 9 (commit / push) and Step 10 (PR — or, in --on-top-of-pr mode, updating the target PR instead of creating one). Never commit unattended in this mode.


B1 — Tag + Registry

Step 1: Tag the merged fabric commit

git -C <fabric-repo-path> tag v<VERSION> <merge-commit-sha>
git -C <fabric-repo-path> push upstream v<VERSION>

(upstream = whichever remote points at tetherto/qvac-fabric-llm.cpp — it may be named origin in a direct clone; check git remote -v.) Verify: git -C <fabric-repo-path> show v<VERSION> --stat

Step 2: Compute SHA512 of the tag tarball

curl -fsSL https://github.com/tetherto/qvac-fabric-llm.cpp/archive/v<VERSION>.tar.gz -o /tmp/fabric-tag.tgz
vcpkg hash /tmp/fabric-tag.tgz

Step 3: Update registry portfile (tetherto/qvac-registry-vcpkg)

  • ports/qvac-fabric/portfile.cmake: update SHA512 to the tag tarball's hash. That is the only edit. REF is already REF v${VERSION}, parameterized off the port's vcpkg.json — do not replace it with a literal REF v<VERSION>; de-parameterizing the portfile is a review rejection.
  • ports/qvac-fabric/vcpkg.json: "version": "<VERSION>" — bumping this is what moves the ref.
  • Update versions DB:
    vcpkg --x-builtin-registry-versions-dir=versions x-add-version qvac-fabric
    
Then clean up after x-add-version — the diff must stay minimal

x-add-version writes "port-version": 0 into the new entry, and may retro-add it to entries that are already published. Both are noise. Delete the field from the new entry, and revert any change to an existing entry.

The rule: omit port-version when it is 0 — absent already means 0. Include it only when non-zero, which a <VERSION>#1 port rebuild genuinely needs.

Do not argue from file-local style. The file's entries are inconsistent (some carry the field, some do not), so "most of them have it" is not evidence that it is required — and normalizing a published entry to match is a drive-by edit to something live.

Prove it is unnecessary with the idempotency re-run, after the edit:

vcpkg --x-builtin-registry-versions-dir=versions x-add-version qvac-fabric --verbose
# expect: "version <VERSION> is already in …versions/q-/qvac-fabric.json"
#         "No files were updated for qvac-fabric"

That output means vcpkg considers the entry complete and correct. If it instead rewrites the file or demands --overwrite-version, the entry really is wrong — fix it rather than forcing it.

vcpkg x-add-version --verify does not exist (error: unexpected switch: --verify). The idempotency re-run above is the check; do not go looking for a --verify flag.

Assert this diff shape before pushing. Anything beyond it is noise:

ports/qvac-fabric/portfile.cmake | 2 +-      (SHA512 only)
ports/qvac-fabric/vcpkg.json     | 2 +-      (version only)
versions/baseline.json           | 2 +-      (baseline string only)
versions/q-/qvac-fabric.json     | 4 ++++    (pure insertion)

A +4/-2-shaped hunk in qvac-fabric.json means an existing entry was modified — revert that part. Leave baseline.json's pre-existing per-entry port-version keys alone: that file's convention is its own, and the rollout changes only the baseline string.

After any amend, re-assert the recorded git-tree — it must equal the git-tree in the new entry:

git rev-parse HEAD:ports/qvac-fabric

Step 4: Verify the portfile locally — mandatory, isolated, and reported

This step is not optional and not satisfiable by a green exit code. Read the trap first.

The false pass — a clean exit here usually means nothing was built

vcpkg's install plan is satisfied by any installed version of the port. A shared install root holding an older qvac-fabric short-circuits the whole thing:

The following packages are already installed:
    qvac-fabric[core,llama]:x64-linux@9840.0.1
Total install time: 156 us
All requested installations completed successfully in: 156 us

That is exit 0 having verified nothing — no download, no hash check, no compile, and the new version never mentioned. A fast, clean, cheerful exit is the failure signature here, not success. (The same reason --overwrite-version does not re-install locally: only a clean root rebuilds.)

Always isolate the roots

An existing local vcpkg checkout is fine — it is the install root that must be fresh, not the tool. Isolation is the part that matters; pick the host triplet for whatever machine you are on.

GH_TOKEN=$(gh auth token) vcpkg install qvac-fabric \
  --overlay-ports=<path-to-qvac-registry-vcpkg-clone>/ports \
  --triplet <host-triplet> \
  --x-install-root=<tmp>/vcpkg-installed \
  --x-buildtrees-root=<tmp>/vcpkg-bt \
  --x-packages-root=<tmp>/vcpkg-pkg
Positive assertions — all three must appear, each naming <VERSION>
Must appear in the logProves
Downloading …/archive/v<VERSION>.tar.gz + Successfully downloadedthe new tag was actually fetched
Building qvac-fabric[…]@<VERSION>the plan resolved the new version, not a cached one
All requested installations completed successfullythe port builds

A SHA512 mismatch is a hard failure before the build, so reaching "Building" is itself the hash gate passing. Sanity-check the elapsed time too: a real build is minutes. Microseconds or seconds means you hit the false pass above.

Re-verification after an amend

If git rev-parse HEAD:ports/qvac-fabric is unchanged, the build result still holds — a versions/-only amend (e.g. removing port-version) needs no re-run. If the ports/ tree changed, re-run this step.

Step 5: Open registry PR

Push branch to tetherto/qvac-registry-vcpkg, create PR.

Report the verification when you present the PR. State that it was built locally and how: version built, triplet, features, that the install root was isolated, and that the three Step 4 assertions held. A registry PR handed over without that line is incomplete — the reviewer cannot tell a real verification from the false pass above.

If Step 4 was skipped or could not run, say so explicitly. Silence reads as "verified".

Step 5b: Strip the overlay from the target PR — --on-top-of-pr only

Skip this step entirely in the default mode; Step 8 covers the validation-branch case.

Why here. The revert lands after B1 has published <VERSION>, and it is immediately followed by B2's bump on the same branch, in the same push (5b.5 below). The overlay comes off and the floors go up as one transition, so the PR never sits in a state where it resolves neither the overlay nor the new version.

Before the target PR is closed or merged. If it has been closed or already merged by the time you get here, --on-top-of-pr has nothing to attach to: fall back to the default separate-PR path (Steps 6–10 as written), and say plainly in the report that the fallback was used and why. The tag is already pushed at this point — do not stall the rollout waiting on a decision.

  1. Check out the target PR at its current head. gh pr checkout <n> --repo tetherto/qvac, then pull. Commits may well have landed since Phase A — do not assume the head you saw then.

  2. Locate the overlay commit, first hit wins:

    git log --grep="^Rollout-Overlay:" --format=%H origin/main..HEAD
    git log --diff-filter=A --format=%H -- vcpkg-overlays/ports/qvac-fabric
    

    …then the SHA supplied by the user. The first is the trailer Phase A Step 5 writes; the second catches overlays predating it. More than one hit means the overlay was re-pinned mid-validation — revert them newest first, or the older revert conflicts with the newer state.

  3. Revert.

    git revert --no-commit <overlay-sha>
    

    If it conflicts — later commits touched those files — fall back to explicit removal (git rm -r vcpkg-overlays/ports/qvac-fabric, and Edit the overlay-ports key out of each of the 7 vcpkg-configuration.json) and say the fallback was used. A conflicting revert usually means something else edited the overlay, which is worth a human looking at.

  4. The completeness sweep. Every check runs; report all failures together. It is deliberately repo-wide rather than scoped to the 7 consumers:

    Diff against the merge base, not origin/main's tip — set it once and reuse:

    BASE=$(git merge-base origin/main HEAD)
    
    CheckPasses when
    the revert is the exact inverse of the overlaygit diff <overlay-sha>^ HEAD -- vcpkg-overlays "packages/*/vcpkg-configuration.json" → empty
    port subtree gonegit diff $BASE -- vcpkg-overlays → empty
    no key left anywheregit grep -l "overlay-ports" -- "**/vcpkg-configuration.json" → no output
    no baseline driftgit diff $BASE -- "packages/*/vcpkg-configuration.json" → empty
    nothing left behindgit status --porcelain → clean

Shortened here. Read the whole file on GitHub.

Signals

GitHub stars
601
Forks
111
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
rollout-phase-b
Source
github.com/tetherto/qvac