跳到正文

CI 门禁

CI Gates

本页只有英文版,按原文显示。

This file documents the quality gates wired into .github/workflows/library-ci.yml and their current enforcement state. Updating this file is part of changing a gate's enforcement level — if you flip a step from continue-on-error: true to required, update the table here in the same PR.

Gate matrix

GateToolScopeStatePromotion blocker
Package unit testspnpm -r test (vitest)All workspacesREQUIRED—
Legacy root vitest suite./node_modules/.bin/vitestRoot src/NOT WIREDRoot src/ was removed when the code moved into packages/, so the root run finds no tests and no workflow invokes it.
Bundle size budgetssize-limitcore/react/vue/ngWARNBudgets are still pre-build measurements off src/index.ts; core currently reports ~242 kB against a 30 kB limit.
Publish hygiene (exports/types/main)publint --strictcore/react/vue/ngWARNpublint needs dist/ to verify file existence under pkg.exports.
Type-resolution (ESM)@arethetypeswrong/cli (attw)core/agent-runtime/vue/react/ngREQUIRED—
UI interaction + token gatespnpm lint:ui (ESLint + stylelint, bulk suppressions)packages/vue/srcREQUIRED (ratchet)—
Per-package buildpnpm -r build (tsc)All workspacesWARNThe recursive run also targets packages/desktop-electron (electron-builder) and examples/angular-universal (ng build, fails outside its own workspace), so it is not a library gate. The publishable packages are built by the test job instead.
Coverage threshold@vitest/coverage-v8RootNOT WIREDIntentionally deferred until Round 1E lands real engine code worth covering.

attw profile

pnpm lint:types runs scripts/lint-types.mjs, which pins one profile for every publishable package:

  • --profile esm-only — each publishable package ships ESM only, and Node 22+ supports require() of ESM, so the legacy Node CJS resolution rules only produce false alarms here.
  • --exclude-entrypoints style.css for the Vue package — a CSS entry is not JavaScript and can never resolve as a type.

The package list and the per-package exclusions live in that script, so the profile has a single definition. Gate the new package by adding it there.

UI interaction + token gates

pnpm lint:ui enforces the interaction audit (P0-5) and ADR 0004 on packages/vue/src:

  • ESLint (eslint.config.mjs, Vue template rules Biome does not cover): no native title= / :title on HTML elements (use aria-label + <BaseTooltip>), no window.confirm/alert/prompt (use useToast().showUndo() or an in-place confirm), no raw <textarea> outside BaseTextarea, <button> needs type.
  • stylelint (stylelint.config.mjs, plugin scripts/lint/stylelint-plugin-klc.mjs): no raw colours, no raw px for padding/margin/gap/radius/font-size, no raw z-index, no textarea resize handle, no transition: all. Fallbacks inside var(--token, fallback) are allowed.

Existing debt is recorded in eslint-suppressions.json and stylelint-suppressions.json; CI fails only when a file gains violations. After fixing debt run pnpm lint:ui:prune (ESLint also fails on stale suppressions, so the baseline only ratchets down).

Per-package bundle budgets

These are pre-build, source-based measurements (size-limit reads from packages/<x>/src/index.ts). They will be re-measured against dist/index.js once the build pipeline lands.

PackageLimit (gzip)Today's measurementRationale
core30 KB~1.6 KBHeadroom for signals + controller scaffolding + a minimal indicator set.
react8 KB~0.8 KBThin wrapper around core (use* hooks); peers excluded from the budget.
vue8 KB~1.1 KBThin composable layer; vue excluded from the budget.
angular10 KB~1.2 KBAngular decorator runtime traditionally adds 1–2 KB; extra slack on top.

If a limit trips, do not raise it reflexively — first check whether a tree shake or peer-dep externalization is the right move.

Publishing readiness (Round 1E target)

When Round 1E lands and we are ready to publish:

  1. library-ci.yml already builds every package under the type-resolution gate before running it; lint:publish and size:packages reuse those builds.
  2. Flip the remaining warn-only gates to required (remove continue-on-error: true) once their blockers in the matrix are resolved.
  3. Re-baseline the size budgets against the real dist/index.js.
  4. Verify publishConfig.provenance: true is honored by the publish workflow (it requires id-token: write permission and OIDC-enabled runners — already the default on ubuntu-latest).

Promotion order for the remaining warn-only gates

lint:types (attw) is required as of #189. The two gates still warn-only:

  1. publint --strict — cheapest to fix, catches broken exports/main/ types paths before a user ever installs the package. Failure here means the package is literally unimportable. It passes today; promote it once the exports shape of every publishable package is settled.
  2. size-limit — still measured pre-build off src/index.ts, so the budgets must be re-baselined against the real dist/ output before the gate can be trusted.

Promoting attw first was deliberate: unlike the other two, it was the only gate that could see the type/runtime mismatch shipped in #189 (extensionless relative imports, .vue declaration specifiers, a leaked workspace path), so it had to go live before the packages could be called correct.

Local commands

pnpm test:packages     # workspace tests
pnpm size:packages     # bundle budgets
pnpm lint:publish      # publint --strict, every publishable package
pnpm lint:types        # attw (esm-only profile), every publishable package

本页内容