Repository navigation
build(repo): mutate only each package's *.workflow.ts files at the release gate - #86
Merged
Merged
Conversation
…lease gate The planner lists every package's workflow files from one glob in stryker.shared.ts, and the gate hands that list to Stryker with --mutate, which replaces whatever mutate a package config sets. The planner refuses, by package, its own stryker.mutate, a mutation script other than exactly 'stryker run', a mutating package without workflow files, workflow files no mutation script covers, and a workspace with none (ruling A-8, A-8b). Judgment surfaces (CONST-W3), owner-directed: scripts/mutation-shards.ts, release-gate.yml, stryker.shared.ts, apps/site stryker config and field, turbo.json test:scripts inputs.
The planner walk skips the shared config's ignorePatterns plus node_modules and .stryker-tmp, and refuses by name a workflow path with a comma or glob metacharacter, which the comma-joined --mutate list would split or expand. turbo 2.11.7 forwarded '-- --mutate' to the generate dependency, so the gate now runs mutation's dependencies from turbo's dry-run graph first and then mutation alone with --only; the package name goes through env. The plan is superseded by a new dated plan carrying the corrections.
ryanleecode
approved these changes
Oct 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Until now the release gate mutated whatever set each package chose for itself. A package can no longer widen or narrow that set: Stryker mutates exactly each package's
*.workflow.tsdecision files, and any other way of setting the scope is refused before Stryker starts. Plan:docs/plans/2026-10-09-1040-build-mutate-only-workflows-plan.md(ruling A-8, A-8b, A-8c). It supersedes the1002plan from the first commit.What changes
stryker.shared.tsexportsWORKFLOW_FILES = '**/*.workflow.ts'. ItsStrykerConfigimport is type-only, so the plan job, which has nonode_modules, can load it through Deno.scripts/mutation-shards.ts). For each package it lists that package's workflow files and outputs them. The walk skips the shared config'signorePatterns(reports,coverage,dist) plusnode_modulesand.stryker-tmp; there is no second list. The plan output is asshards=[{"package","mutate":[...]}]. It refuses, by package directory and with a tagged refusal:OwnMutate: the package'spackage.jsonsetsstryker.mutate.MutationScriptNotStrykerRun: themutationscript is not exactlystryker run. That closes the route where a script passes its own-m.NoWorkflowFiles: the package mutates but has no workflow files.WorkflowFilesNotMutated: the package has workflow files but no package name ormutationscript.UnsafeWorkflowPath: a workflow path contains a comma or a glob metacharacter (*?[]{}()!\), which the comma-joined--mutatelist would split or expand.NoWorkflowFilesInWorkspace: the workspace has no workflow files, so the set would be empty.release-gate.yml). The matrix runs overshard.PACKAGEandMUTATE(join(matrix.shard.mutate, ',')) both go throughenv. The gate runs in two steps:turbo run mutation --filter="$PACKAGE" --dry=jsonlists other thanmutationitself. Today that is@endgame/site#generate.turbo run mutation --filter="$PACKAGE" --only -- --mutate "$MUTATE". The fork merges the CLI record over the config file (mergeConfig(file, cli), vendored stryker-js 17.0.2dist/main.mjs:84525), so the list replaces anymutatea config sets.strykerfield. Its config isexport default packageStrykerConfig.stryker.shared.tsis added to the//#test:scriptsinputs, because the planner tests now import it.Evidence
Turbo passthrough (A-8c).
turbo run mutation --filter=@endgame/site --dry=json -- --mutate xon turbo 2.11.7 gives the args to the dependency too:With the fix, the dependency step's dry run gives
{"taskId":"@endgame/site#generate","cliArguments":[]}. The mutation step's dry run (--only ... -- --mutate src/api/check-health.workflow.ts) gives:I also ran the dependency step for real: it derived
@endgame/site#generate, ran it, and exited 0.actionlinton the workflow exits 0. Stryker never started.A1 probe: the fork's real
mergeConfig, under Node, given a config widened tosrc/**/*.tsand a CLI list of the two workflow files, returned exactly those two files. The probe file was deleted afterwards.A3 run: the planner run on the real tree with no
node_modulesprinted@endgame/sitewithsrc/api/check-health.workflow.ts,src/features/guestbook/moderate-guestbook-entry.workflow.tsandsrc/features/guestbook/sign-guestbook.workflow.ts, and exited 0.pnpm test:scripts: 14 passed, 0 failed. Of these, 11 are planner tests over real temporary workspaces, compared against hand-written plans.Sabotage (CONST-T10). I made these edits to
mutation-shards.ts:OwnMutaterefusal;WorkflowFilesNotMutatedrefusal;NoWorkflowFilesrefusal;script.length < 0, which never fires;**/*.tswith no excludes.Result:
FAILED | 3 passed | 9 failed. All 9 planner tests went red; the 3 that passed arecheck-branch-rulestests. The file was restored byte-identical. Log:.cache/sabotage.log(not committed).Sabotage for A-8c. I dropped the shared
ignorePatternsfrom the walk and made the unsafe-path check match nothing. Result:FAILED | 12 passed | 2 failed; the two that failed are the dist-copy test and the unsafe-path test. The file was restored byte-identical. Log:.cache/sabotage-a8c.log(not committed).pnpm check:ci: exit 0 on76394c9; it was also exit 0 on3db7628. A fresh worktree first needspnpm run prepare, because theeffecttsgooxlint plugin exists only after that step.Stryker was not run locally. The release gate runs it on
main.Judgment surfaces (CONST-W3)
scripts/mutation-shards.ts,scripts/mutation-shards.test.ts,.github/workflows/release-gate.yml,stryker.shared.ts,apps/site/stryker.config.tsand thestrykerfield inapps/site/package.json, and the//#test:scriptsinputs inturbo.json. The owner directed these changes (ruling A-8, A-8b).