Skip to content

feat(ci): adopt @quality-gate/core for multi-metric merge gate#47

Merged
AlexandreCamillo merged 21 commits into
mainfrom
feat/coverage-bootstrap
May 29, 2026
Merged

feat(ci): adopt @quality-gate/core for multi-metric merge gate#47
AlexandreCamillo merged 21 commits into
mainfrom
feat/coverage-bootstrap

Conversation

@AlexandreCamillo

Copy link
Copy Markdown
Collaborator

Summary

  • Replaces the bespoke scripts/coverage-ratchet.ts + coverage-data orphan branch with the upstream alkg-cloud/quality-gate engine, pinned at commit 192fcaf3.
  • Adds five ratcheted metrics: coverage (vitest), lint (biome), duplication (jscpd), file_size, security (pnpm audit).
  • New orphan branch quality-metrics replaces coverage-data (latter to be deleted post-merge).
  • See docs/quality-gate.md for the full contract.

What's in this PR

Change File(s)
Config quality-gate.config.json
Adapter .quality-gate/adapter.sh, .quality-gate/README.md
Workflows .github/workflows/quality-gate-pr.yml, .github/workflows/quality-gate-main.yml
Bespoke ratchet removed scripts/coverage-ratchet.ts, scripts/lib/coverage-compare.ts, tests/unit/scripts/coverage-compare.test.ts, ratchet step in .github/workflows/test.yml
Docs docs/quality-gate.md (new), docs/ci.md, docs/testing.md, docs/INDEX.md, README.md (badges)
Tolerance epsilon: 0.1 (preserves the 0.10pp drift tolerance from the bespoke ratchet)

Why we clone+build the upstream engine

@quality-gate/core is not published to npm (E404) and the upstream repo has no GitHub releases/tags + does not commit dist/. Both workflows clone the upstream at the pinned SHA 192fcaf3 and run pnpm install && pnpm run build before invoking node /tmp/qg-core/dist/cli.js. To bump the pin, edit the SHA in both workflow files.

Bootstrap behaviour (this PR's CI)

The first PR runs without a baseline. The engine returns bootstrap: true, skips ratchet comparisons, and only blocks on critical audit vulnerabilities. Local dry-run confirms:

```json
{ "gate_passed": true, "bootstrap": true, "regressions": 0, "warnings": 0 }
```

Current snapshot: coverage 58.99%, duplication 18.75%, lint 0 errors, 9 files >500 lines, security 0 critical / 0 high / 3 moderate / 0 low.

Merging this PR populates quality-metrics on first push; subsequent PRs are ratcheted normally.

Required follow-up after merge (user-driven, NOT in this PR)

  1. Wait for `quality-gate-baseline` to finish on the merge commit, then verify the orphan branch exists: `git fetch origin && git branch -r | grep quality-metrics`.
  2. Verify badges resolve: open the two `img.shields.io/endpoint?url=…/quality-metrics/badges/*.json` URLs in a browser.
  3. Delete the legacy orphan branch: `git push origin --delete coverage-data`.
  4. Update branch protection on `main`: replace any required check that referred to the bespoke coverage ratchet with `quality-gate / quality-gate`. Keep `lint`, `typecheck`, `build`, `test-coverage`, `e2e`.

Test plan

  • `quality-gate / quality-gate` check appears on this PR and passes (bootstrap mode, only blocks on `critical`).
  • Sticky PR comment is posted showing all 5 metrics.
  • After merge, `quality-gate-baseline` workflow runs and creates `quality-metrics` branch with `baseline.json`, `badges/coverage.json`, `badges/quality.json`, `README.md`.
  • After merge, README badges resolve.

@github-actions

github-actions Bot commented May 29, 2026

Copy link
Copy Markdown

🚦 Quality Gate — ⚙️ BOOTSTRAP

Adapter: markup@0.1.0 · Tools: vitest, biome, jscpd, pnpm-audit
Baseline: pending — will be created on first merge to default branch.

Metric Baseline PR Δ Status
Coverage 59.06%
Duplication 19.35%
Lint (total) 3181
Oversized 9
Security 0 crit

⚙️ Bootstrap pending — baseline will be created on first merge to default branch. Critical security issues still block.

… drop unused QG_MODE

- Cache /tmp/qg-core keyed on the pinned upstream SHA so subsequent CI runs skip the ~25s clone+install+build on hit.
- _meta.json reads adapter.name / adapter.version from quality-gate.config.json (jq) so the snapshot the engine stores in baseline.json cannot drift from what the adapter reports.
- Drop QG_MODE env from both workflows: the adapter never reads it and the contract didn't document it.
@AlexandreCamillo

Copy link
Copy Markdown
Collaborator Author

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

…tion

- Split actions/cache into restore + conditional save so a crashed install/build
  never persists a partial /tmp/qg-core (missing dist/cli.js) under the fixed-SHA
  key. With the prior single-action cache, the post-step saved on job failure too,
  poisoning the cache for every later run until manual eviction or a pin bump.
- Add src/app/landing + src/components/landing to the jscpd --ignore list so the
  duplication metric matches the coverage and file_size exclusions. Landing is a
  presentation-heavy marketing surface whose repeated section markup inflated the
  metric without signalling real duplication debt.
The endpoint cropped a screenshot using pinCoords. After the
comment-only annotation flow landed in PR #44, no new annotation row
has either field set — the endpoint was unreachable for anything
created after the cutover. Removing the route, the sharp-backed
cropRegion helper, the unit test, sharp's pnpm "built-dependencies"
entry, and every doc cross-reference (agent-loop endpoints + INDEX,
api INDEX, feature catalog, stack, storage, routes, ci, frontend
INDEX, task-rules, testing).

Legacy annotation rows with screenshotPath + pinCoords populated will
no longer be reachable through this URL — orchestrators relying on it
should fetch the full screenshot via /api/annotations/[id]/screenshot.
…scpd

Extract the shared prelude + engine clone/build into
.github/actions/quality-gate-prepare so the PR and baseline workflows
no longer duplicate ~50 lines and the engine SHA lives in one place.
Run jscpd from a pinned devDependency instead of re-downloading via npx
each run, write adapter scratch files to a mktemp dir (keeping the repo
clean and out of jscpd's own scan), and count file lines with awk so a
missing trailing newline can't slip a file past the size gate.
@AlexandreCamillo

Copy link
Copy Markdown
Collaborator Author

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

🤖 Generated with Claude Code

…n; restore git clone for engine

GitHub validates ${{ }} expressions everywhere in an action manifest,
including the top-level description, where the runner context is not
available — this failed the workflow at load time. State the path in
plain prose instead.

Also revert the engine fetch to the proven `git clone --depth 1` form
rather than `git init` + fetch-by-SHA, which is not equivalent on a cold
runner.
…omment file

If the suite or engine build fails, the adapter never writes
pr-comment.md, and the always() comment step would itself fail and bury
the real error. Gate it on an explicit existence check instead.
@AlexandreCamillo AlexandreCamillo merged commit 17686cb into main May 29, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant