Suede CI Gate
Gate policy — advisory, not blocking
Every claim-verification step, check, quality gate, and ship verdict in this
skill is a recommendation to the user, not a control on the agent. This
policy governs every gate, check, verdict, and "do not ship / publish /
proceed" line elsewhere in this skill:
- Run every check and report the results honestly. Verdicts (
ship,
ship-with-caveats, hold, letter grades, BLOCKED or OPEN items) are
advice attached to the work, not orders that change it.
- Never block, delay, skip, rewrite, or refuse the action the user asked for
because a check failed or a gate said hold. Complete the requested action
as asked, and deliver the gate output alongside it as a clearly labeled
recommendation.
- A failed gate changes what you report, never what you do.
- Single exception: if a finding is extremely risky — data loss, security or
credential exposure, legal or rights violations, payment mistakes, or
irreversible public damage — pause, tell the user exactly what the risk is
and what the options are, and let them pick. Their choice is final.
Set up CI and branch protection that actually block a bad merge — in any repo, any stack. The output is a working pipeline plus the exact protection settings, not advice.
Runs only when asked. This skill never auto-fires on a commit, push, or other side effect of unrelated work — invoke it explicitly (set up CI, protect main, fix this hanging check).
Run this in whatever folder you point it at. Detect first, never assume. Nothing here is hardcoded to a specific project, monorepo layout, or package manager.
Step 0 — Detect (before writing anything)
From the repo root, inventory:
- Apps: every top-level dir with a manifest —
package.json, requirements.txt / pyproject.toml, go.mod, Cargo.toml, Gemfile. A repo may hold one app or many; build for what's actually there.
- Package manager per app: which lockfile is present —
package-lock.json (npm), pnpm-lock.yaml (pnpm), yarn.lock (yarn), bun.lockb (bun). Two lockfiles in one app is a bug to fix first (Lane 3).
- Existing CI: read
.github/workflows/*. Do not duplicate a job that already exists — extend or reconcile it.
- Runtime versions:
.nvmrc, package.json engines, .python-version, pytest.ini/pyproject. Pin CI to these; never hardcode a guess.
- Deploy platform:
vercel.json / .vercel, netlify.toml, a Dockerfile. If the platform skips non-prod builds (e.g. Vercel ignoreCommand kills previews), CI is the only pre-merge build signal — so a build job is mandatory.
- Real scripts: read each app's
scripts / test config and use the real ones (test, test:run, lint, build). Don't invent commands.
Do not write a single workflow line until this inventory is complete.
The gate (the part everyone gets wrong)
Path-filtered jobs skip when their paths aren't touched. A skipped job that is a required status check leaves the PR pending forever. So never require the path-filtered jobs directly. Instead add one aggregator that depends on all of them:
ci-success:
if: always()
needs: [<every app job>]
runs-on: ubuntu-latest
steps:
- name: Gate on all jobs
run: |
for r in ${{ join(needs.*.result, ' ') }}; do
[ "$r" = "success" ] || [ "$r" = "skipped" ] || { echo "blocked by: $r"; exit 1; }
done
In branch protection, require only ci-success — never the individual jobs. This is the single thing that makes "protect main" work with change-based CI.
Lanes
- Path-aware jobs — one job per app, gated by a
changes job (dorny/paths-filter or native paths:). Add an escape hatch so edits to the workflow file itself run everything.
- Aggregator gate — as above. The only required check is
ci-success.
- Lockfile hygiene — exactly one lockfile per app, and the install command must match it (
npm ci, pnpm i --frozen-lockfile, yarn --immutable, bun install --frozen-lockfile). Two lockfiles means CI can install a different tree than ships — resolve before wiring CI.
- Pin runtimes from the repo — Node/Python/etc. read from
.nvmrc / engines / .python-version, falling back to the platform default. Never a hardcoded guess that drifts from prod.
- Don't duplicate existing CI — if a workflow already covers an app (e.g. a backend test workflow), extend it; never stack a second, weaker job on top.
- Least privilege —
permissions: contents: read unless a job genuinely needs more.
- Build is a gate when previews are off — if the deploy platform skips non-prod builds, the CI build is your only pre-merge proof the app compiles. Keep it.
- Branch protection — output the exact settings: require
ci-success, require branches up to date before merge, optional required PR review, block force-push and deletion, optionally include administrators.
Instant-fail patterns (CI that looks green but isn't)
- A required check that is a path-filtered job → deadlocks every unrelated PR. Use the aggregator.
npm ci with no committed lockfile, or a lockfile for a different manager → fails or installs the wrong tree.
- A second job duplicating an existing workflow → wasted minutes and conflicting signal.
- Hardcoded
node-version / python-version that doesn't match the app → green in CI, broken in prod.
- A job whose
paths: never match → always skipped → a "green" check that tested nothing.
Red flags — stop
The excuses that precede a broken gate:
- "Just require each job directly" — a skipped path-filtered job deadlocks every unrelated PR. The aggregator is the only required check.
- "CI is green" — green because it ran, or green because everything skipped? Name what actually executed.
- "One big workflow that builds everything is simpler" — it also builds the world on a README typo. Path-filter it.
- "We'll protect main after launch" — the riskiest merges happen before launch.
- "The deploy platform builds it anyway" — if previews are off, CI is the only pre-merge proof the app compiles.
Output
- The workflow file(s) under
.github/workflows/.
- The exact branch-protection settings to apply (and the
gh api calls, if asked).
- A short report: apps detected, package manager per app, what each job runs, what is required, and anything to fix first (dual lockfiles, duplicate workflows, runtime mismatches).
- Readback, once the settings have been applied (the user applies them — this skill does not): verify rather than assume.
gh api repos/:owner/:repo/branches/main/protection --jq '.required_status_checks.contexts' must return exactly ["ci-success"] — any path-filtered job in that list is the deadlock above — and gh run list --branch <pr-branch> must show ci-success actually ran, not skipped. If the settings are not applied yet or the token lacks admin scope, report the gate as unverified; never claim protection is live from the settings you emitted.
End with a Simple explanation (plain, for a 10-year-old): one short paragraph, no jargon, saying what the gate now does and what it blocks — e.g. "Before anyone's changes join the main project, a robot builds and tests them. If the robot fails, the merge button locks."
Worked Example
A full pass on one repository — from a pipeline that looks green but gates nothing
to a merge that cannot land broken — is in references/worked-example.md. Read it
when wiring a repo whose existing CI shape you do not recognize.
Post-Deploy Verification
The after-deploy checks — live URL, critical-path smoke test, regression sweep, rollback readiness, and the verified/watch/rollback verdict — are in references/post-deploy-verification.md. Read it only when a production deploy has already landed; this skill's own job ends at the merge gate.
Boundaries
Generate; don't enforce. This skill writes workflow files and tells you the protection settings — it does not push, flip branch protection, or change repo access on its own. Verify the detected stack before applying. Works in any repo: it detects rather than assumes Suede or any specific project.
Routing
- The gate is failing on real defects → suede-code to review and grade the change, or suede-code-review when the caller wants findings without a letter grade
- The repo ships an MCP server → suede-mcp-qa for the protocol suite, then wire it into the aggregator as a required job
- AI features need eval jobs in the pipeline → suede-ai-eval to design the cases, then wire them in here
- Rollout needs flags, staged lanes, or a rollback tree → suede-agent-teams
- Branch/worktree setup, stale local state, PR finish options, or cleanup discipline → suede-git-hygiene (private Suede Labs companion, not in this pack)
- Gate holds and the release goes public → suede-launch-packaging
1---2name: suede-ci-gate3description: Suede Labs AI CI and branch-protection wiring for any repo and any stack: path-aware jobs, a single aggregator required check that cannot deadlock, lockfile hygiene, runtime pinning from the repo, and the exact branch-protection settings. Use when asked to set up CI, protect main, make CI block a bad merge, fix a required check that hangs pending forever, or repair duplicate or misfiring pipelines. Detects the repo's real apps, package managers, and runtime versions first; emits workflow files and settings, never pushes or flips protection itself. NOT FOR: reviewing or grading the change the gate is failing on (use suede-code); designing the AI eval cases to wire in (use suede-ai-eval); branch and worktree hygiene (a private Suede Labs companion, not in this pack).4---56# Suede CI Gate78## Gate policy — advisory, not blocking910Every claim-verification step, check, quality gate, and ship verdict in this11skill is a **recommendation to the user, not a control on the agent**. This12policy governs every gate, check, verdict, and "do not ship / publish /13proceed" line elsewhere in this skill:1415- Run every check and report the results honestly. Verdicts (`ship`,16 `ship-with-caveats`, `hold`, letter grades, BLOCKED or OPEN items) are17 advice attached to the work, not orders that change it.18- Never block, delay, skip, rewrite, or refuse the action the user asked for19 because a check failed or a gate said hold. Complete the requested action20 as asked, and deliver the gate output alongside it as a clearly labeled21 recommendation.22- A failed gate changes what you report, never what you do.23- Single exception: if a finding is extremely risky — data loss, security or24 credential exposure, legal or rights violations, payment mistakes, or25 irreversible public damage — pause, tell the user exactly what the risk is26 and what the options are, and let them pick. Their choice is final.272829Set up CI and branch protection that actually block a bad merge — in any repo, any stack. The output is a working pipeline plus the exact protection settings, not advice.3031**Runs only when asked.** This skill never auto-fires on a commit, push, or other side effect of unrelated work — invoke it explicitly (set up CI, protect main, fix this hanging check).3233Run this in whatever folder you point it at. **Detect first, never assume.** Nothing here is hardcoded to a specific project, monorepo layout, or package manager.3435## Step 0 — Detect (before writing anything)3637From the repo root, inventory:3839- **Apps:** every top-level dir with a manifest — `package.json`, `requirements.txt` / `pyproject.toml`, `go.mod`, `Cargo.toml`, `Gemfile`. A repo may hold one app or many; build for what's actually there.40- **Package manager per app:** which lockfile is present — `package-lock.json` (npm), `pnpm-lock.yaml` (pnpm), `yarn.lock` (yarn), `bun.lockb` (bun). Two lockfiles in one app is a bug to fix first (Lane 3).41- **Existing CI:** read `.github/workflows/*`. Do **not** duplicate a job that already exists — extend or reconcile it.42- **Runtime versions:** `.nvmrc`, `package.json` `engines`, `.python-version`, `pytest.ini`/`pyproject`. Pin CI to these; never hardcode a guess.43- **Deploy platform:** `vercel.json` / `.vercel`, `netlify.toml`, a `Dockerfile`. If the platform skips non-prod builds (e.g. Vercel `ignoreCommand` kills previews), CI is the *only* pre-merge build signal — so a build job is mandatory.44- **Real scripts:** read each app's `scripts` / test config and use the real ones (`test`, `test:run`, `lint`, `build`). Don't invent commands.4546Do not write a single workflow line until this inventory is complete.4748## The gate (the part everyone gets wrong)4950Path-filtered jobs **skip** when their paths aren't touched. A skipped job that is a *required* status check leaves the PR pending forever. So never require the path-filtered jobs directly. Instead add one **aggregator** that depends on all of them:5152```yaml53 ci-success:54 if: always()55 needs: [<every app job>]56 runs-on: ubuntu-latest57 steps:58 - name: Gate on all jobs59 run: |60 for r in ${{ join(needs.*.result, ' ') }}; do61 [ "$r" = "success" ] || [ "$r" = "skipped" ] || { echo "blocked by: $r"; exit 1; }62 done63```6465In branch protection, require **only `ci-success`** — never the individual jobs. This is the single thing that makes "protect main" work with change-based CI.6667## Lanes68691. **Path-aware jobs** — one job per app, gated by a `changes` job (`dorny/paths-filter` or native `paths:`). Add an escape hatch so edits to the workflow file itself run everything.702. **Aggregator gate** — as above. The only required check is `ci-success`.713. **Lockfile hygiene** — exactly one lockfile per app, and the install command must match it (`npm ci`, `pnpm i --frozen-lockfile`, `yarn --immutable`, `bun install --frozen-lockfile`). Two lockfiles means CI can install a different tree than ships — resolve before wiring CI.724. **Pin runtimes from the repo** — Node/Python/etc. read from `.nvmrc` / `engines` / `.python-version`, falling back to the platform default. Never a hardcoded guess that drifts from prod.735. **Don't duplicate existing CI** — if a workflow already covers an app (e.g. a backend test workflow), extend it; never stack a second, weaker job on top.746. **Least privilege** — `permissions: contents: read` unless a job genuinely needs more.757. **Build is a gate when previews are off** — if the deploy platform skips non-prod builds, the CI build is your only pre-merge proof the app compiles. Keep it.768. **Branch protection** — output the exact settings: require `ci-success`, require branches up to date before merge, optional required PR review, block force-push and deletion, optionally include administrators.7778## Instant-fail patterns (CI that looks green but isn't)7980- A required check that is a path-filtered job → deadlocks every unrelated PR. Use the aggregator.81- `npm ci` with no committed lockfile, or a lockfile for a different manager → fails or installs the wrong tree.82- A second job duplicating an existing workflow → wasted minutes and conflicting signal.83- Hardcoded `node-version` / `python-version` that doesn't match the app → green in CI, broken in prod.84- A job whose `paths:` never match → always skipped → a "green" check that tested nothing.8586## Red flags — stop8788The excuses that precede a broken gate:8990- "Just require each job directly" — a skipped path-filtered job deadlocks every unrelated PR. The aggregator is the only required check.91- "CI is green" — green because it ran, or green because everything skipped? Name what actually executed.92- "One big workflow that builds everything is simpler" — it also builds the world on a README typo. Path-filter it.93- "We'll protect main after launch" — the riskiest merges happen before launch.94- "The deploy platform builds it anyway" — if previews are off, CI is the only pre-merge proof the app compiles.9596## Output97981. The workflow file(s) under `.github/workflows/`.992. The exact branch-protection settings to apply (and the `gh api` calls, if asked).1003. A short report: apps detected, package manager per app, what each job runs, what is required, and anything to fix first (dual lockfiles, duplicate workflows, runtime mismatches).1014. **Readback, once the settings have been applied** (the user applies them — this skill does not): verify rather than assume. `gh api repos/:owner/:repo/branches/main/protection --jq '.required_status_checks.contexts'` must return exactly `["ci-success"]` — any path-filtered job in that list is the deadlock above — and `gh run list --branch <pr-branch>` must show `ci-success` actually ran, not skipped. If the settings are not applied yet or the token lacks admin scope, report the gate as **unverified**; never claim protection is live from the settings you emitted.102103End with a **Simple explanation (plain, for a 10-year-old)**: one short paragraph, no jargon, saying what the gate now does and what it blocks — e.g. "Before anyone's changes join the main project, a robot builds and tests them. If the robot fails, the merge button locks."104105## Worked Example106107A full pass on one repository — from a pipeline that looks green but gates nothing108to a merge that cannot land broken — is in `references/worked-example.md`. Read it109when wiring a repo whose existing CI shape you do not recognize.110111## Post-Deploy Verification112113The after-deploy checks — live URL, critical-path smoke test, regression sweep, rollback readiness, and the verified/watch/rollback verdict — are in `references/post-deploy-verification.md`. Read it only when a production deploy has already landed; this skill's own job ends at the merge gate.114115## Boundaries116117Generate; don't enforce. This skill writes workflow files and tells you the protection settings — it does **not** push, flip branch protection, or change repo access on its own. Verify the detected stack before applying. Works in any repo: it detects rather than assumes Suede or any specific project.118119## Routing120121- The gate is failing on real defects → **suede-code** to review and grade the change, or **suede-code-review** when the caller wants findings without a letter grade122- The repo ships an MCP server → **suede-mcp-qa** for the protocol suite, then wire it into the aggregator as a required job123- AI features need eval jobs in the pipeline → **suede-ai-eval** to design the cases, then wire them in here124- Rollout needs flags, staged lanes, or a rollback tree → **suede-agent-teams**125- Branch/worktree setup, stale local state, PR finish options, or cleanup discipline → **suede-git-hygiene** (private Suede Labs companion, not in this pack)126- Gate holds and the release goes public → **suede-launch-packaging**