Release v3.8.30 (#4267)

Release v3.8.30 — see CHANGELOG.md [3.8.30] for the full release notes.
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-06-20 07:09:43 -03:00
committed by GitHub
parent ab8096071c
commit db362b0126
356 changed files with 14268 additions and 1140 deletions

View File

@@ -25,7 +25,7 @@ jobs:
packages: write
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
persist-credentials: false

View File

@@ -31,7 +31,7 @@ jobs:
API_KEY_SECRET: ci-lint-api-key-secret-long
DISABLE_SQLITE_AUTO_BACKUP: "true"
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6
@@ -79,7 +79,7 @@ jobs:
contents: read
security-events: read
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6
@@ -161,7 +161,7 @@ jobs:
# fetch-depth: 0 — the OpenAPI breaking-change gate (oasdiff) reads the base
# spec via `git show <base_ref>:docs/reference/openapi.yaml`; a shallow clone
# would lack the base ref and the gate would self-skip (base-unresolved).
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
fetch-depth: 0
@@ -265,7 +265,7 @@ jobs:
name: Docs Sync (Strict)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6
@@ -297,7 +297,7 @@ jobs:
# existing doc corpus is brought up to style. Promote to blocking once it converges.
continue-on-error: true
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6
@@ -320,7 +320,7 @@ jobs:
name: i18n UI Coverage
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6
@@ -336,7 +336,7 @@ jobs:
outputs:
langs: ${{ steps.langs.outputs.langs }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- id: langs
@@ -355,7 +355,7 @@ jobs:
lang: ${{ fromJson(needs.i18n-matrix.outputs.langs) }}
needs: i18n-matrix
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-python@v6
@@ -384,7 +384,7 @@ jobs:
if: ${{ github.event_name == 'pull_request' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
fetch-depth: 0
@@ -414,7 +414,7 @@ jobs:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6
@@ -447,7 +447,7 @@ jobs:
env:
JWT_SECRET: ci-build-secret-with-sufficient-length-for-validation
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6
@@ -472,7 +472,7 @@ jobs:
JWT_SECRET: ci-build-secret-with-sufficient-length-for-validation
CSC_IDENTITY_AUTO_DISCOVERY: "false"
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6
@@ -507,7 +507,7 @@ jobs:
API_KEY_SECRET: ci-test-api-key-secret-long
DISABLE_SQLITE_AUTO_BACKUP: "true"
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6
@@ -516,7 +516,7 @@ jobs:
cache: npm
- run: npm ci
- run: npm run check:node-runtime
- run: node --max-old-space-size=4096 --import tsx --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 --test-shard=${{ matrix.shard }}/8 tests/unit/*.test.ts "tests/unit/{api,auth,authz,build,cli,cli-helper,compression,correctness,cors,dashboard,db,db-adapters,docs,gamification,guardrails,lib,mcp,runtime,security,services,settings,shared,ui}/**/*.test.ts"
- run: node --max-old-space-size=4096 --import tsx --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 --test-shard=${{ matrix.shard }}/8 tests/unit/*.test.ts "tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,gamification,guardrails,lib,mcp,runtime,security,services,settings,shared,ui}/**/*.test.ts"
test-vitest:
name: Vitest (MCP / autoCombo / UI components)
@@ -528,7 +528,7 @@ jobs:
API_KEY_SECRET: ci-test-api-key-secret-long
DISABLE_SQLITE_AUTO_BACKUP: "true"
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6
@@ -548,7 +548,7 @@ jobs:
node-24-compat:
name: Node 24 Compatibility (${{ matrix.shard }}/2)
runs-on: ubuntu-latest
timeout-minutes: 15
timeout-minutes: 25
needs: build
strategy:
fail-fast: false
@@ -559,7 +559,7 @@ jobs:
API_KEY_SECRET: ci-test-api-key-secret-long
DISABLE_SQLITE_AUTO_BACKUP: "true"
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6
@@ -569,12 +569,12 @@ jobs:
- run: npm ci
- run: npm run check:node-runtime
- run: npm run build
- run: node --import tsx --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 --test-shard=${{ matrix.shard }}/2 tests/unit/*.test.ts "tests/unit/{api,auth,authz,build,cli,cli-helper,compression,correctness,cors,dashboard,db,db-adapters,docs,gamification,guardrails,lib,mcp,runtime,security,services,settings,shared,ui}/**/*.test.ts"
- run: node --import tsx --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 --test-shard=${{ matrix.shard }}/2 tests/unit/*.test.ts "tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,gamification,guardrails,lib,mcp,runtime,security,services,settings,shared,ui}/**/*.test.ts"
node-26-compat:
name: Node 26 Compatibility (${{ matrix.shard }}/2)
runs-on: ubuntu-latest
timeout-minutes: 15
timeout-minutes: 25
needs: build
strategy:
fail-fast: false
@@ -585,7 +585,7 @@ jobs:
API_KEY_SECRET: ci-test-api-key-secret-long
DISABLE_SQLITE_AUTO_BACKUP: "true"
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6
@@ -595,7 +595,7 @@ jobs:
- run: npm ci
- run: npm run check:node-runtime
- run: npm run build
- run: node --import tsx --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 --test-shard=${{ matrix.shard }}/2 tests/unit/*.test.ts "tests/unit/{api,auth,authz,build,cli,cli-helper,compression,correctness,cors,dashboard,db,db-adapters,docs,gamification,guardrails,lib,mcp,runtime,security,services,settings,shared,ui}/**/*.test.ts"
- run: node --import tsx --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 --test-shard=${{ matrix.shard }}/2 tests/unit/*.test.ts "tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,gamification,guardrails,lib,mcp,runtime,security,services,settings,shared,ui}/**/*.test.ts"
test-coverage-shard:
name: Coverage Shard (${{ matrix.shard }}/8)
@@ -611,7 +611,7 @@ jobs:
API_KEY_SECRET: ci-test-api-key-secret-long
DISABLE_SQLITE_AUTO_BACKUP: "true"
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6
@@ -636,7 +636,7 @@ jobs:
--exclude=tests/** \
--exclude=**/*.test.* \
node --max-old-space-size=4096 --import tsx --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 \
--test-shard=${{ matrix.shard }}/8 tests/unit/*.test.ts "tests/unit/{api,auth,authz,build,cli,cli-helper,compression,correctness,cors,dashboard,db,db-adapters,docs,gamification,guardrails,lib,mcp,runtime,security,services,settings,shared,ui}/**/*.test.ts"
--test-shard=${{ matrix.shard }}/8 tests/unit/*.test.ts "tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,gamification,guardrails,lib,mcp,runtime,security,services,settings,shared,ui}/**/*.test.ts"
- name: Upload raw shard coverage
if: always()
uses: actions/upload-artifact@v7
@@ -655,7 +655,7 @@ jobs:
JWT_SECRET: ci-test-secret-with-sufficient-length-for-validation
API_KEY_SECRET: ci-test-api-key-secret-long
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6
@@ -733,7 +733,7 @@ jobs:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
fetch-depth: 0
@@ -853,7 +853,7 @@ jobs:
DISABLE_SQLITE_AUTO_BACKUP: "true"
OMNIROUTE_PLAYWRIGHT_SKIP_BUILD: "1"
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6
@@ -896,7 +896,7 @@ jobs:
DATA_DIR: /tmp/omniroute-ci-${{ matrix.shard }}
DISABLE_SQLITE_AUTO_BACKUP: "true"
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6
@@ -916,7 +916,7 @@ jobs:
API_KEY_SECRET: ci-test-api-key-secret-long
DISABLE_SQLITE_AUTO_BACKUP: "true"
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6

View File

@@ -30,7 +30,7 @@ jobs:
actions: read # Required for Claude to read CI results on PRs
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
persist-credentials: false
fetch-depth: 1

View File

@@ -19,7 +19,7 @@ jobs:
actions: read
contents: read
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2

View File

@@ -15,7 +15,7 @@ jobs:
JWT_SECRET: ci-build-secret-with-sufficient-length-for-validation
API_KEY_SECRET: ci-api-key-secret-with-sufficient-length-aaaa
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0

View File

@@ -42,7 +42,7 @@ jobs:
IMAGE_NAME: diegosouzapw/omniroute
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
persist-credentials: false
ref: ${{ github.event_name == 'workflow_dispatch' && format('refs/tags/v{0}', inputs.version) || '' }}
@@ -143,7 +143,7 @@ jobs:
GHCR_IMAGE_NAME: ghcr.io/diegosouzapw/omniroute
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
persist-credentials: false
ref: ${{ github.event_name == 'workflow_dispatch' && format('refs/tags/v{0}', inputs.version) || '' }}
@@ -243,7 +243,7 @@ jobs:
PROMOTE_LATEST: ${{ needs.prepare.outputs.promote_latest }}
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
persist-credentials: false
ref: ${{ github.event_name == 'workflow_dispatch' && format('refs/tags/v{0}', inputs.version) || '' }}

View File

@@ -27,7 +27,7 @@ jobs:
version: ${{ steps.validate.outputs.version }}
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
persist-credentials: false
fetch-depth: 0
@@ -84,7 +84,7 @@ jobs:
deb_ext: .deb
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- name: Setup Node
@@ -216,7 +216,7 @@ jobs:
contents: write # softprops/action-gh-release creates the GitHub Release
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
persist-credentials: false
fetch-depth: 0

View File

@@ -0,0 +1,63 @@
name: Mutation Redundancy (disableBail, on-demand)
# One-off measurement to UNBLOCK R1 (test-redundancy prune). The nightly mutation run
# (nightly-mutation.yml) bails on the first kill, so `killedBy` lists only the FIRST
# killer — 🟠 redundant is understated and 🟢 unique overstated (see the caveat in
# scripts/quality/mutation-radiography.mjs). This workflow re-runs the SAME combo +
# chatCore leaf batches with stryker.disablebail.json (disableBail:true, incremental:false)
# so `killedBy` lists EVERY killer. Feed the uploaded reports to
# `node scripts/quality/mutation-radiography.mjs --candidates mutation-nobail-*/mutation.json`
# to get the accurate R1 prune-candidate list (🔴 empty 🟠 redundant) for human review.
#
# Batches mirror the nightly's leaf decomposition (d/e/f/g/h/i) rather than 2 mega-batches:
# disableBail is MORE expensive than bail (it never stops early), and Stryker only writes
# mutation.json on a SUCCESSFUL finish — a batch cancelled at the cap produces NO data — so
# smaller batches each fit the 300min headroom and run in parallel. auth/accountFallback and
# the security quartet are out of scope: R1 targets the combo/chatCore leaves.
on:
workflow_dispatch:
permissions:
contents: read
jobs:
stryker-nobail:
name: Stryker disableBail (batch ${{ matrix.batch.name }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
batch:
- name: d
mutate: "open-sse/services/combo/comboStructure.ts,open-sse/services/combo/autoStrategy.ts,open-sse/services/combo/validateQuality.ts"
- name: e
mutate: "open-sse/services/combo/shadowRouting.ts,open-sse/services/combo/targetSorters.ts,open-sse/services/combo/comboPredicates.ts,open-sse/services/combo/rrState.ts,open-sse/services/combo/comboData.ts"
- name: f
mutate: "open-sse/services/combo/quotaScoring.ts,open-sse/services/combo/quotaStrategies.ts"
- name: g
mutate: "open-sse/handlers/chatCore/comboContextCache.ts,open-sse/handlers/chatCore/idempotency.ts,open-sse/handlers/chatCore/passthroughHelpers.ts,open-sse/handlers/chatCore/responseHeaders.ts,open-sse/handlers/chatCore/sanitization.ts,open-sse/handlers/chatCore/upstreamTimeouts.ts"
- name: h
mutate: "open-sse/handlers/chatCore/headers.ts,open-sse/handlers/chatCore/logTruncation.ts,open-sse/handlers/chatCore/memoryExtraction.ts,open-sse/handlers/chatCore/nonStreamingSse.ts,open-sse/handlers/chatCore/passthroughToolNames.ts,open-sse/handlers/chatCore/executorHelpers.ts"
- name: i
mutate: "open-sse/handlers/chatCore/telemetryHelpers.ts,open-sse/handlers/chatCore/memorySkillsInjection.ts,open-sse/handlers/chatCore/semanticCache.ts"
timeout-minutes: 300
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6
with:
node-version: "24"
cache: npm
- run: npm ci
- name: Run Stryker (disableBail)
env:
BATCH_MUTATE: ${{ matrix.batch.mutate }}
run: npx stryker run --config-file stryker.disablebail.json --mutate "$BATCH_MUTATE"
- name: Upload mutation report
if: always()
uses: actions/upload-artifact@v7
with:
name: mutation-nobail-${{ matrix.batch.name }}
path: reports/mutation/
if-no-files-found: warn
retention-days: 14

View File

@@ -12,7 +12,7 @@ jobs:
name: promptfoo — injection guard (block mode, no secret)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6
@@ -61,7 +61,7 @@ jobs:
echo "run=false" >> "$GITHUB_OUTPUT"
echo "::notice::PROMPTFOO_PROVIDER_KEY not set — skipping garak probes (advisory)."
fi
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
if: steps.gate.outputs.run == 'true'

View File

@@ -36,11 +36,15 @@ jobs:
# a successful finish); a job cancelled at the cap writes nothing, so the next run is cold
# again — an infinite never-seeds loop. actions/cache is also branch-scoped, so each branch
# (incl. release) must seed its OWN cache via a run with enough headroom. Measured cold runs
# (run 27801802713): a/b never finished even isolated; c=180min CANCELLED (4 modules),
# d=180min CANCELLED (3 combo modules), g=142min, h=132min, e=66, f=45, i=33. So a/b/c/d get
# 350min (job max 360) and g/h a 240min variance buffer; once seeded, later nightlies only
# re-test changed mutants and fit well under the default. NOTE: batch c's old "2 modules ~2h"
# assumption went stale when c grew to 4 modules — keep this budget in sync with the matrix.
# Measured cold totals (run 27801802713, extrapolated from the % at the 180/350 cancel point):
# auth ~375min (2301 mutants — EXCEEDS the 360min job max even isolated), accountFallback
# ~358min (1441), the c security quartet ~348min (1163), d ~197min (1316), g=142, h=132, e=66,
# f=45, i=33. The widely-covered modules blow the budget because the tap-runner re-runs every
# covering test file per mutant (a perTest fixed cost over ~138 test files, times thousands of
# mutants). A flat timeout bump cannot rescue auth (>360min max) — so the three over-budget
# batches are SPLIT so each half fits: auth->a1/a2 and accountFallback->b1/b2 by mutation range
# (`file:startLine-endLine`), the c quartet->c1/c2 by module pair. Splitting also seeds each
# sub-batch's own incremental cache, after which nightlies re-test only changed mutants.
# Full coverage every night in parallel; wall-clock = the slowest batch's cold run until seeded.
# Runs at stryker concurrency=4 with per-process DATA_DIR isolation
# (tests/_setup/isolateDataDir.ts) — see _concurrency_comment in stryker.conf.json.
@@ -48,23 +52,38 @@ jobs:
fail-fast: false
matrix:
batch:
# Per-batch `timeout` (minutes) tiers the cold-seeding budget by measured cost — see the
# cold-run measurements in the comment above. Batches without the key default to 180.
# a/b (large isolated modules) + c/d (4 and 3 modules, observed to exceed 180min cold) get
# 350 (GitHub-hosted job max is 360); g/h (142/132min cold — within runner-variance distance
# of the 180 cap) get a 240 buffer. e/f/i (33-66min) stay at the 180 default.
- name: a
mutate: "src/sse/services/auth.ts"
timeout: 350
- name: b
mutate: "open-sse/services/accountFallback.ts"
timeout: 350
- name: c
mutate: "src/server/authz/routeGuard.ts,src/shared/utils/circuitBreaker.ts,open-sse/utils/error.ts,open-sse/utils/publicCreds.ts"
timeout: 350
# Per-batch `timeout` (minutes) tiers the cold-seeding budget by measured cost; batches
# without the key default to 180. Cold-run profiling (run 27801802713) showed the
# widely-covered modules need FAR more than the 180 cap because the tap-runner re-runs every
# covering test file per mutant: auth ~375min (2301 mutants, EXCEEDS the 360min GitHub job
# max even isolated), accountFallback ~358min, the c security quartet ~348min — none fit a
# single job. So auth/accountFallback are split by MUTATION RANGE (`file:startLine-endLine`,
# ~half the mutants each) into a1/a2, b1/b2; the c quartet is split by MODULE pair into
# c1/c2. d (3 combo modules, ~197min) stays whole. Split-heavy batches get 300min headroom
# for their cold seeding run; once each batch completes once and writes
# stryker-incremental.json, later runs re-test only changed mutants and finish far faster.
# g/h (142/132min cold) keep a 240 buffer; e/f/i (33-66min) keep the 180 default.
- name: a1
mutate: "src/sse/services/auth.ts:1-1109"
timeout: 300
- name: a2
mutate: "src/sse/services/auth.ts:1110-2218"
timeout: 300
- name: b1
mutate: "open-sse/services/accountFallback.ts:1-863"
timeout: 300
- name: b2
mutate: "open-sse/services/accountFallback.ts:864-1726"
timeout: 300
- name: c1
mutate: "src/server/authz/routeGuard.ts,src/shared/utils/circuitBreaker.ts"
timeout: 300
- name: c2
mutate: "open-sse/utils/error.ts,open-sse/utils/publicCreds.ts"
timeout: 300
- name: d
mutate: "open-sse/services/combo/comboStructure.ts,open-sse/services/combo/autoStrategy.ts,open-sse/services/combo/validateQuality.ts"
timeout: 350
timeout: 300
- name: e
mutate: "open-sse/services/combo/shadowRouting.ts,open-sse/services/combo/targetSorters.ts,open-sse/services/combo/comboPredicates.ts,open-sse/services/combo/rrState.ts,open-sse/services/combo/comboData.ts"
- name: f
@@ -77,11 +96,15 @@ jobs:
timeout: 240
- name: i
mutate: "open-sse/handlers/chatCore/telemetryHelpers.ts,open-sse/handlers/chatCore/memorySkillsInjection.ts,open-sse/handlers/chatCore/semanticCache.ts"
# Per-batch budget: a/b/c/d override to 350min, g/h to 240min; the rest default to 180min.
# `matrix.batch.timeout` is null for batches without the key -> `|| 180`.
# Per-batch budget: split-heavy batches (a1/a2/b1/b2/c1/c2/d) override to 300min, g/h to 240min;
# the rest default to 180min. `matrix.batch.timeout` is null for batches without the key -> `|| 180`.
# NOTE: a1+a2 both mutate auth.ts (disjoint line ranges) and b1+b2 both mutate accountFallback.ts;
# when merging the per-batch mutation.json for radiography/scores, same-file mutants from sibling
# ranges must be UNIONED (scripts/check/check-mutation-ratchet.mjs::measureMutationScores and
# scripts/quality/mutation-radiography.mjs both merge per file).
timeout-minutes: ${{ matrix.batch.timeout || 180 }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6
@@ -109,3 +132,32 @@ jobs:
path: reports/mutation/
if-no-files-found: warn
retention-days: 14
# Aggregation gate (T3): each split batch emits a PARTIAL view of a mutated file
# (auth.ts lives in a1+a2, accountFallback in b1+b2), so a PER-BATCH ratchet would
# only ever see half a file vs the whole-file baseline. This job runs AFTER every
# batch, downloads all reports, and ratchets the MERGED per-module scores
# (check-mutation-ratchet UNIONS same-file mutants across reports) against the
# dedicatedGate `mutationScore.*` floors in quality-baseline.json (seeded ~2pt below
# the first full measurement). Blocking: a module dropping below its floor fails the
# run. Missing reports (e.g. an artifact-upload flake) are skipped, never failed.
mutation-ratchet:
name: Mutation score ratchet (blocking)
needs: stryker
if: always()
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: actions/setup-node@v6
with:
node-version: "24"
- name: Download all mutation reports
uses: actions/download-artifact@v8
with:
pattern: mutation-report-*
path: reports/all
- name: Ratchet merged per-module mutation scores
run: node scripts/check/check-mutation-ratchet.mjs reports/all/*/mutation.json --ratchet

View File

@@ -10,7 +10,7 @@ jobs:
property-random-seed:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6

View File

@@ -12,7 +12,7 @@ jobs:
name: Heap-growth gate
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6
@@ -26,7 +26,7 @@ jobs:
name: Resilience chaos (fault injection)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6
@@ -40,7 +40,7 @@ jobs:
name: k6 load/soak
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6
@@ -74,3 +74,37 @@ jobs:
- name: Stop server
if: always()
run: kill "$(cat server.pid)" || true
a11y:
name: A11y axe (nightly, freeze-and-alert)
runs-on: ubuntu-latest
# The Playwright webServer (`start` mode) builds Next via build-next-isolated.mjs and
# boots the standalone server itself (waits on /api/monitoring/health, 15min webServer
# timeout). Unlike the per-PR test-e2e job, this nightly job has no pre-built artifact,
# so it self-builds — hence the generous job timeout. REQUIRE_AXE=1 makes the suite run
# the real axe analysis (the 4 page tests are gated to nightly so per-PR e2e stays fast)
# and makes the meta-test fail loudly if @axe-core/playwright ever goes missing.
timeout-minutes: 30
env:
JWT_SECRET: ci-test-secret-with-sufficient-length-for-validation
API_KEY_SECRET: ci-test-api-key-secret-long
DISABLE_SQLITE_AUTO_BACKUP: "true"
REQUIRE_AXE: "1"
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6
with:
node-version: "24"
cache: npm
- run: npm ci
- name: Cache Playwright browsers
uses: actions/cache@v5.0.5
with:
path: ~/.cache/ms-playwright
key: playwright-chromium-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
restore-keys: playwright-chromium-${{ runner.os }}-
- run: npx playwright install --with-deps chromium
- name: Run axe a11y suite (self-building webServer)
run: npx playwright test tests/e2e/a11y.spec.ts

View File

@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6

View File

@@ -55,7 +55,7 @@ jobs:
packages: write # publish to npm.pkg.github.com
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
persist-credentials: false
# Need full tag history to compare against highest semver when
@@ -202,7 +202,7 @@ jobs:
id-token: write # npm provenance
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
persist-credentials: false

View File

@@ -32,7 +32,7 @@ jobs:
matrix:
node: ["22", "24"]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6
@@ -49,7 +49,7 @@ jobs:
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6

View File

@@ -32,7 +32,7 @@ jobs:
matrix:
node: ["20", "22", "24"]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6
@@ -48,7 +48,7 @@ jobs:
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v6

View File

@@ -27,7 +27,7 @@ jobs:
API_KEY_SECRET: ci-lint-api-key-secret-long
DISABLE_SQLITE_AUTO_BACKUP: "true"
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
fetch-depth: 0
persist-credentials: false

View File

@@ -21,7 +21,7 @@ jobs:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
persist-credentials: false

View File

@@ -12,7 +12,7 @@ jobs:
container:
image: semgrep/semgrep
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Run semgrep (advisory)

View File

@@ -37,7 +37,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: Setup Node
uses: actions/setup-node@v6