Problem / Why
.github/workflows/ci.yml runs the entire C# quality toolchain inside a single
quality-gates job on windows-latest with a 60-minute timeout. That job executes
five expensive stages strictly sequentially:
nuget restore
dotnet csharpier check . (formatting)
msbuild /t:Build with EnableNETAnalyzers + EnforceCodeStyleInBuild (analyzers)
msbuild /t:Rebuild with TreatWarningsAsErrors=true (nullable/type-check)
vstest.console.exe with /EnableCodeCoverage (MSTest suite)
Wall-clock CI latency is therefore the sum of all five stages. Stages 3 and 4 are
both full solution compiles of a 19-project solution, and stage 4 uses /t:Rebuild,
so it performs a complete recompile from scratch. A formatting violation is not
reported until after restore completes, and a test failure is not reported until
after two full builds have finished.
Two additional consequences of the monolithic shape:
- No independent failure signal. All five gates report as one required status
check named Format, build, analyze, and test, so a red check does not identify
which gate failed without opening the log.
- No independent re-dispatch. A transient failure in one stage requires
re-running the entire 60-minute job.
Proposed Behavior
Decompose the monolithic quality-gates job into independent gates that GitHub
Actions schedules concurrently, so wall-clock CI latency approaches the duration of
the slowest single gate rather than the sum of all gates.
Per .claude/skills/orchestrate/SKILL.md (## GitHub Actions Reusable Workflows),
each gate ships as a callable reusable workflow named _<name>.yml declaring both
on: workflow_call: and on: workflow_dispatch:, and the orchestrator workflow
references them via uses: ./.github/workflows/_<name>.yml with no inline steps:
of its own. Any file that must cross a job boundary uses explicit
actions/upload-artifact + actions/download-artifact; cross-job filesystem
reliance is not implicit.
Acceptance Criteria
Constraints & Risks
- Branch-protection coupling (blocking risk). The
main ruleset (id 18572843,
strict_required_status_checks_policy: true) requires exactly two contexts:
actionlint and Format, build, analyze, and test. Splitting the job removes the
second context. Until the ruleset is updated, every PR will block on a check that
can never report. The ruleset update is automatable via
gh api --method PUT repos/drmoisan/TaskMaster/rulesets/18572843 (the session token
holds repo scope and repository admin: true), but it is an outward-facing change
to the repository's merge policy and must be applied deliberately, in the correct
order relative to the merge of this change.
- Windows runner setup cost is paid per job. Each parallel job repeats checkout,
setup-dotnet, setup-msbuild, setup-nuget, and nuget restore. If the split is
naive, the added fixed cost per job can offset the parallelism gain. Caching
(actions/cache on packages and ~/.nuget/packages) and a decision about whether
to share build output via artifact upload rather than rebuilding are both load-bearing.
- Two full compiles are inherent to the current design. The analyzer gate uses
/t:Build and the nullable gate uses /t:Rebuild; the /t:Rebuild choice is
deliberate and documented in-file (MSBuild's incremental up-to-date check does not
invalidate on a command-line property change alone). Whether these two compiles can
share output, must stay separate, or should run concurrently on separate runners is
the central design question.
- Test gate needs built assemblies.
vstest.console.exe discovers *.Test.dll
under bin/$BUILD_CONFIGURATION. Running the test gate in a separate job requires
either its own build or a downloaded build artifact.
- Local vstest discovery hazard. Recursive
*.Test.dll discovery can pick up
stale agent-worktree builds; the CI-side discovery filter must not regress.
- Concurrency group.
cancel-in-progress: true is set at the workflow level and
must continue to behave correctly across the reusable-workflow boundary.
- Actions concurrency limits. Windows runner concurrency on the account may cap
how many windows-latest jobs actually run at once, bounding the realized speedup.
Test Conditions
Source
From: docs/features/potential/2026-08-14-ci-parallel-job-split.md
Problem / Why
.github/workflows/ci.ymlruns the entire C# quality toolchain inside a singlequality-gatesjob onwindows-latestwith a 60-minute timeout. That job executesfive expensive stages strictly sequentially:
nuget restoredotnet csharpier check .(formatting)msbuild /t:BuildwithEnableNETAnalyzers+EnforceCodeStyleInBuild(analyzers)msbuild /t:RebuildwithTreatWarningsAsErrors=true(nullable/type-check)vstest.console.exewith/EnableCodeCoverage(MSTest suite)Wall-clock CI latency is therefore the sum of all five stages. Stages 3 and 4 are
both full solution compiles of a 19-project solution, and stage 4 uses
/t:Rebuild,so it performs a complete recompile from scratch. A formatting violation is not
reported until after restore completes, and a test failure is not reported until
after two full builds have finished.
Two additional consequences of the monolithic shape:
check named
Format, build, analyze, and test, so a red check does not identifywhich gate failed without opening the log.
re-running the entire 60-minute job.
Proposed Behavior
Decompose the monolithic
quality-gatesjob into independent gates that GitHubActions schedules concurrently, so wall-clock CI latency approaches the duration of
the slowest single gate rather than the sum of all gates.
Per
.claude/skills/orchestrate/SKILL.md(## GitHub Actions Reusable Workflows),each gate ships as a callable reusable workflow named
_<name>.ymldeclaring bothon: workflow_call:andon: workflow_dispatch:, and the orchestrator workflowreferences them via
uses: ./.github/workflows/_<name>.ymlwith no inlinesteps:of its own. Any file that must cross a job boundary uses explicit
actions/upload-artifact+actions/download-artifact; cross-job filesystemreliance is not implicit.
Acceptance Criteria
MSTest gate each run as separate GitHub Actions jobs with no
needs:edgeforcing them to serialize, except where an edge is required to consume an
uploaded build artifact.
_<name>.ymldeclaring bothon: workflow_call:andon: workflow_dispatch:.ci.ymlbecomes an orchestrator workflow containing onlyuses:referencesand contains no inline
steps:.actions/upload-artifact+actions/download-artifact.mainbranch ruleset'srequired_status_checkscontexts are updated tomatch the new job names, with no window in which a merge can bypass a gate.
.github/workflows/README.mddocuments the per-stageworkflow_dispatchprocedure and the branch-protection rename procedure.
modified-workflow-needs-green-run.quality-gatesjob is still enforced afterthe split; no check is dropped, weakened, or made non-required.
Constraints & Risks
mainruleset (id18572843,strict_required_status_checks_policy: true) requires exactly two contexts:actionlintandFormat, build, analyze, and test. Splitting the job removes thesecond context. Until the ruleset is updated, every PR will block on a check that
can never report. The ruleset update is automatable via
gh api --method PUT repos/drmoisan/TaskMaster/rulesets/18572843(the session tokenholds
reposcope and repositoryadmin: true), but it is an outward-facing changeto the repository's merge policy and must be applied deliberately, in the correct
order relative to the merge of this change.
setup-dotnet,setup-msbuild,setup-nuget, andnuget restore. If the split isnaive, the added fixed cost per job can offset the parallelism gain. Caching
(
actions/cacheonpackagesand~/.nuget/packages) and a decision about whetherto share build output via artifact upload rather than rebuilding are both load-bearing.
/t:Buildand the nullable gate uses/t:Rebuild; the/t:Rebuildchoice isdeliberate and documented in-file (MSBuild's incremental up-to-date check does not
invalidate on a command-line property change alone). Whether these two compiles can
share output, must stay separate, or should run concurrently on separate runners is
the central design question.
vstest.console.exediscovers*.Test.dllunder
bin/$BUILD_CONFIGURATION. Running the test gate in a separate job requireseither its own build or a downloaded build artifact.
*.Test.dlldiscovery can pick upstale agent-worktree builds; the CI-side discovery filter must not regress.
cancel-in-progress: trueis set at the workflow level andmust continue to behave correctly across the reusable-workflow boundary.
how many
windows-latestjobs actually run at once, bounding the realized speedup.Test Conditions
actionlintpasses against every new and modified workflow file._<name>.ymlis independently dispatchable viaworkflow_dispatchandsucceeds standalone.
distinct red check.
current sequential baseline and recorded as evidence.
pwshstep leaks a residual non-zero$LASTEXITCODEper.claude/rules/ci-workflows.md.Source
From: docs/features/potential/2026-08-14-ci-parallel-job-split.md