{
  "$schema": "https://raw.githubusercontent.com/stryker-mutator/stryker-js/master/packages/api/schema/stryker-core.json",
  "_comment": "Shared Stryker base for mandrel-platform consumers. Stryker has NO `extends` option — its config reader loads exactly one config file and merges CLI arguments over it, and `extends` is not among the top-level properties its schema accepts, so a config declaring one is reported as an unknown option and otherwise ignored. Adopt this base by importing it by package specifier and spreading it into a `stryker.config.mjs`; the single worked example lives in the mandrel-platform README under `stryker.base.json`. testRunner, mutate, and per-repo thresholds stay consumer-tunable. Asserted by scripts/stryker-base-config.test.mjs.",
  "packageManager": "pnpm",
  "reporters": ["html", "clear-text", "progress"],
  "coverageAnalysis": "perTest",
  "ignoreStatic": true,
  "cleanTempDir": true,
  "concurrency_comment": "Capped at 1 deliberately. Stryker defaults `concurrency` to `n-1` logical cores (`n` when `n <= 4`), so a consumer scope that spreads this base and names no `concurrency` of its own silently takes one worker per core — 17 on an 18-core host. Where a runner host is shared between repos, that lane costs cores no caller budgeted, and the damage lands on a neighbouring tenant as a failed required check nobody can diagnose from inside it. Omission is therefore the unsafe direction, and this pins the safe one: more parallelism is opt-in, either by spreading a later `concurrency` key or via `--concurrency` on the CLI, which completely replaces the config value. Consumers on dedicated CI should raise it — see the README. Asserted by scripts/stryker-base-config.test.mjs.",
  "concurrency": 1,
  "disableBail_comment": "Bail is OFF deliberately. Under Stryker's default bail the vitest runner can score a mutant Survived having completed zero of its covering tests, so a run reports a number nothing measured and a committed baseline becomes a floor under it. Every mutant now runs its full covering set, which lengthens the run — the three timeouts below are sized for that and must move together with this flag. Asserted by scripts/stryker-base-config.test.mjs.",
  "disableBail": true,
  "timeoutMS": 120000,
  "timeoutFactor": 3,
  "dryRunTimeoutMinutes": 15,
  "thresholds_comment": "`break: 50` is unchanged, and deliberately so. It was chosen while bail deflated the measured score (a consumer recorded 53.5% against a real 72.29%), but that deflation runs one way only: a bail-free score is higher, so a break calibrated against a deflated number cannot fail a run it would previously have passed. Raising it fleet-wide would be the very sin the flag above exists to prevent — a fail-closed number nothing measured, imposed on consumers whose real scores are unknown from here. So 50 stays a floor of last resort rather than a calibrated target: after re-deriving a baseline on the bail-free base (docs/patterns.md, 'Adopting the bail-free Stryker base'), set a tighter break locally. `high`/`low` are report-colour bands and fail nothing.",
  "thresholds": {
    "high": 80,
    "low": 60,
    "break": 50
  }
}
