Runs on push/PR to main: npm ci → ESLint (no warnings) → Jest with coverage → upload artifact. Uses permissions: contents: read for supply-chain hardening. Node 20 matches package.json engine requirement.
Runs on push/PR to main: npm ci → ESLint (no warnings) → Jest with coverage → upload artifact. Uses permissions: contents: read for supply-chain hardening. Node 20 matches package.json engine requirement.