Merge Guard Wire — hook a new job into the required status check
qvac-merge-guard / validate-pr is the single required status check on main. Adding a new CI job without wiring it into this aggregate creates a second, disconnected check the branch ruleset never requires — its failures become invisible. This skill is the checklist for wiring a job into the existing aggregate correctly.
Full reference with diagrams and worked examples: docs/ci/MERGE-GUARD.md.
When to use
- Adding any new job/workflow that should block merges — regardless of domain (addon package check, SDK check, docs validation, security scan, licensing gate, anything).
- A PR review flags a new job that isn't reflected in
qvac-merge-guard's pass/fail.
Hard rule: nothing but pr-gate-merge.yml may produce the qvac-merge-guard / validate-pr check name
grep -rl "^ qvac-merge-guard:$" .github/workflows/*.yml
Must return only pr-gate-merge.yml. If any other file has a job id qvac-merge-guard (or a job calling a reusable workflow whose job id is validate-pr), that's a collision — it would silently satisfy the required check without going through the real gate. Fix before doing anything else in this skill.
Step 1 — Read the current job graph
Read .github/workflows/pr-gate-merge.yml, specifically the final qvac-merge-guard job's needs: list and the three with: booleans it passes to public-pr.yml. Read docs/ci/MERGE-GUARD.md for the diagram and worked examples.
Note: sdk-pod-checks reports under two check-context strings (SDK Pod Checks and sdk-pod-checks / SDK Pod Checks) — branch-coverage artifact, not a ruleset requirement. See the doc's "Gotcha" section.
Step 2 — Ask the user which of the three patterns fits — do not silently infer it
Present these three options as an explicit question and let the human pick. Even if the repo state (an existing half-built caller workflow, an existing similar job, etc.) suggests one pattern, don't silently commit to it — surface the choice and its tradeoffs, then proceed with whichever the human confirms:
Folds into an existing category (another instance of a check already gated on, whatever its domain) — if matrix/filter-driven, add it to the
changesjob'sdorny/paths-filtermap; no other wiring needed. Otherwise add the job to an existing category'sneeds:and AND its result into that category's boolean expression.A repeated, growing family of similarly-shaped jobs — more than one or two near-identical jobs differing only by target (one per package, one per platform, one per service, whatever naming pattern). Only one target today? This is not your pattern — use 1 or 3 instead and wire the single job directly. A caller workflow's value is dispatching to multiple targets from one place; building one for a single target is pure indirection. Refactor into a caller once a second target genuinely shows up.
When there truly are multiple targets, check matrix vs. caller before building anything — this repo has both shapes for the same problem:
- Uniform job body across targets (same composite action/steps, only e.g.
workdirdiffers) → use a matrix likesanity-checks(strategy.matrix.include: ...). No caller workflow at all; the matrix job itself is whatqvac-merge-guarddepends on. - Heterogeneous per-target jobs (different
uses:/inputs/secrets per target, or steps that genuinely diverge) → use a caller workflow with one named job per target, likeprebuilds-caller.yml. This shape exists mainly for readability/separation, not because a matrix is technically impossible.
Do not add each family member individually to
qvac-merge-guard'sneeds:. Whichever shape you land on (matrix or caller), wire only that one job/caller intopr-gate-merge.yml, and feed its result intopublic-pr.yml's existing spareintegration-tests-statusinput if unclaimed (don't invent a new one if a spare already fits).- Uniform job body across targets (same composite action/steps, only e.g.
Genuinely new category — a one-off job that isn't a repeated family and doesn't fit any existing boolean. Add the job, add it to
qvac-merge-guard'sneeds:, and either reusepublic-pr.yml's other spare input (build-with-model-status) or add a brand new boolean to both thewith:block andpublic-pr.yml'sworkflow_call.inputs+ shell check block.
Step 3 — Wire it, following the exact code shape in docs/ci/MERGE-GUARD.md
Copy the relevant snippet from the doc verbatim, substituting names. Do not invent a different needs:/if: shape from what's already established. If your new job's checks also need to run on branches Merge Guard doesn't cover (pr-gate-merge.yml is main-only), consider the two-trigger shape (own pull_request/pull_request_target + workflow_call) used by pr-checks-sdk-pod.yml — but understand upfront that produces two check-context strings for one piece of logic on main, and document both wherever the check is referenced by name.
Step 4 — Validate
Because pr-gate-merge.yml is pull_request_target-triggered, it always runs the version on main — a PR editing this file cannot validate its own change pre-merge. Merge first, then open a follow-up PR against updated main and confirm via gh pr checks <n> --repo tetherto/qvac that qvac-merge-guard / validate-pr appears exactly once and reflects the new job's result (force it to fail once to prove the aggregate check goes red).
Verification checklist before calling this done
- Confirmed
grep -rl "^ qvac-merge-guard:$" .github/workflows/*.ymlreturns onlypr-gate-merge.yml— no other workflow produces theqvac-merge-guard / validate-prcheck name. - Picked the right pattern (fold-in / caller-workflow / new-boolean) per Step 2 — did not add a job to
needs:with no aggregation for a repeated family. - If a caller workflow was built, confirmed there are genuinely 2+ targets today — not built preemptively for a single target.
- No new top-level
pull_request_targettrigger with zeroneeds:link intoqvac-merge-guardwas created. - Checked for a spare unused
public-pr.ymlinput (integration-tests-status,build-with-model-status) before adding a new one. - Validated post-merge (not pre-merge) that
qvac-merge-guard / validate-prreflects the new job — exactly one check name, no duplicate. - If the new job has its own independent trigger in addition to being wired into
qvac-merge-guard, confirmed and documented both resulting check-name strings (see thesdk-pod-checksgotcha) — no check-name regex/match written against only one of them.