Skip to content

Canonical-protected: C1 drift + nullable + S1 + D6 + D8 + A1 + CI3 + T1 + T3#135

Merged
Chris-Wolfgang merged 72 commits into
mainfrom
canonical-protected
May 29, 2026
Merged

Canonical-protected: C1 drift + nullable + S1 + D6 + D8 + A1 + CI3 + T1 + T3#135
Chris-Wolfgang merged 72 commits into
mainfrom
canonical-protected

Conversation

@Chris-Wolfgang

@Chris-Wolfgang Chris-Wolfgang commented May 22, 2026

Copy link
Copy Markdown
Owner

Template-drift resolution for IEnumerable-Extensions (initiative C1) plus eight additional canonical initiatives stacked onto this same canonical-protected branch over the C1 campaign. Review carefully before admin-bypass merge — bypass waives all ruleset gates.

Stacked scope (in commit order)

  1. C1 — protected-file drift re-sync (root config / workflow files that trip the pr.yaml Detect .NET Projects guard).
  2. Nullable consolidation<Nullable>enable</Nullable> hoisted into root Directory.Build.props (conditioned to .csproj only); per-csproj <Nullable> lines removed for atomicity (this is why the PR also touches src/tests/examples csproj files — they must merge together with the Directory.Build.props change so Release builds never see a window with nullable annotations but no nullable context).
  3. S1 — CodeQL queries: security-extended added to .github/workflows/codeql.yaml.
  4. D8verify-docs-build job added to .github/workflows/release.yaml; publish-nuget now needs [pack-and-validate, verify-docs-build] so a docs failure blocks the release.
  5. D6versions.json preservation guard added to .github/workflows/docfx.yaml (aborts deploy if previously-published version entries would be lost).
  6. A1Microsoft.CodeAnalysis.PublicApiAnalyzers infrastructure added to Directory.Build.props with opt-in AdditionalFiles globbing (per-project enablement via PublicAPI.Shipped.txt/PublicAPI.Unshipped.txt).
  7. CI3 — Fleet-canonical NuGet metadata defaults (Authors/Company/Copyright/RepositoryType/SymbolPackageFormat/SourceLink/symbols) added to root Directory.Build.props.
  8. T1 — Coverage-report generation step added to docfx.yaml (publishes /coverage/ alongside /api/ on the docs site).
  9. T3 — Stryker mutation-testing workflow (.github/workflows/stryker.yaml) folded in via merge commit (was previously a separate t3-stryker-mutation-testing PR; consolidating onto this branch reduces the per-repo release flow to one admin-bypass merge instead of two).

Why these ride together

The pr.yaml "Detect .NET Projects" guard fires on protected file changes and forces admin-bypass merge. Bypass waives every ruleset rule at once, so each canonical pass costs at most one bypass per repo by design.

Per the C1 plan's standing-branch model, additional canonical changes stack onto this branch rather than fanning out as separate admin-bypass PRs.

🤖 Generated with Claude Code

Closes

This PR lands the protected canonical baseline via admin-bypass. When it merges, GitHub auto-closes the following Tier-1 maintenance sub-issues that were addressed by the protected drift / S1 / D6 / D8 / A1 / CI3 / T1 / T3 content folded into canonical-protected:

The unprotected / feature work + the rest of the Maintenance board sub-issues close via PR #153 (vNext → main) which merges after this PR.

Template-drift resolution for IEnumerable-Extensions — the protected half.
These files trip the pr.yaml "Detect .NET Projects" guard, so they are split
into their own PR that a maintainer merges via admin bypass:

- .editorconfig, BannedSymbols.txt
- .github/workflows/pr.yaml, codeql.yaml, docfx.yaml

Part of #106.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 22, 2026 13:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR is part of the C1 template-drift re-sync work and updates protected repo configuration/workflow files (intended to be merged via admin bypass) to align with the canonical template while tightening some CI security behaviors.

Changes:

  • PR workflow: hardens gitleaks installation (checksum verification), restores .NET workloads, and replaces grep-based TFM parsing with MSBuild property evaluation.
  • DocFX workflow: refactors gh-pages deployment to avoid embedding tokens in remote URLs and improves cleanup behavior.
  • Misc config: updates .editorconfig PowerShell guidance and syncs BannedSymbols.txt header text.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
BannedSymbols.txt Header text synced toward template format.
.editorconfig Removes PowerShell-specific EOL/BOM override and replaces with explanatory comments.
.github/workflows/pr.yaml CI hardening + workload restore + more robust TFM detection + test-stage behavior updates.
.github/workflows/codeql.yaml Adds workload restore step before build for CodeQL runs.
.github/workflows/docfx.yaml Refactors gh-pages deploy authentication/cleanup logic.
Comments suppressed due to low confidence (3)

.github/workflows/pr.yaml:714

  • This step always passes --settings coverlet.runsettings for .NET 5+ frameworks, but coverlet.runsettings is not present in the repo. That will cause dotnet test to fail. Make the runsettings file optional (only pass it when it exists), consistent with scripts/build-pr.ps1.
              if ($fw -match '^net([5-9]|[1-9][0-9]+)\.') {
                dotnet test $testProj.FullName `
                  --configuration Release `
                  --framework $fw `
                  --collect:"XPlat Code Coverage" `
                  --settings coverlet.runsettings `
                  --results-directory "./TestResults" `
                  --logger "console;verbosity=normal"

.github/workflows/pr.yaml:1050

  • dotnet test --settings coverlet.runsettings will fail if coverlet.runsettings is not present in the repo (it currently isn't). Make the runsettings file optional so the workflow doesn't hard-fail on a missing file.
              dotnet test "$test_proj" \
                --configuration Release \
                --framework "$fw" \
                --collect:"XPlat Code Coverage" \
                --settings coverlet.runsettings \
                --results-directory "./TestResults" \
                --logger "console;verbosity=normal" || exit 1

.github/workflows/docfx.yaml:365

  • exit 1 here is inside the try {} block and may prevent the finally {} cleanup (including unsetting the global http.extraheader). Use throw so the step fails while still guaranteeing the finally block runs.
              if (-not (Test-Path '.github/version-picker-template.html')) {
                Write-Error "Error: .github/version-picker-template.html not found; cannot generate root index.html."
                exit 1
              }

Comment thread .github/workflows/pr.yaml
Comment thread .github/workflows/docfx.yaml
Comment thread BannedSymbols.txt Outdated
Comment thread .editorconfig
Add <Nullable>enable</Nullable> to the root Directory.Build.props and
remove the now-redundant per-project <Nullable> lines from every
SDK-style csproj. Nullable reference types are configured in one place;
a newly added project inherits the setting automatically.

Both halves ride this protected branch so they merge atomically — the
Directory.Build.props addition and the csproj removals land together,
so there is never a window where nullable reference types are off.

Legacy non-SDK project files do not import Directory.Build.props and
are left untouched with their explicit settings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Chris-Wolfgang

Copy link
Copy Markdown
Owner Author

Scope added to this PR — nullable consolidation. <Nullable>enable</Nullable> is now set once in the root Directory.Build.props, and the redundant per-csproj <Nullable> lines have been removed from every SDK-style project. Both halves are on this protected branch so they merge atomically — there is never a window where nullable reference types are off. Legacy non-SDK example projects do not import Directory.Build.props and keep their explicit settings.

Legacy non-SDK .csproj files explicitly import Microsoft.Common.props,
so they DO inherit Directory.Build.props — the earlier unconditional
<Nullable>enable</Nullable> reached projects it should not have:

  * F# (.fsproj) / VB (.vbproj) projects — now excluded by conditioning
    the property on '$(MSBuildProjectExtension)' == '.csproj'.
  * legacy non-SDK C# example projects (C# 7.3, no nullable support) —
    given an explicit <Nullable>disable</Nullable> opt-out, restoring
    their pre-hoist state. These are the documented C5 carve-outs.

SDK-style C# projects are unaffected — they still inherit enable from
the single Directory.Build.props.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 22, 2026 19:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 6 comments.

Comment thread .github/workflows/pr.yaml
Comment thread .github/workflows/pr.yaml
Comment thread .github/workflows/pr.yaml
Comment thread BannedSymbols.txt Outdated
Comment thread .editorconfig
Comment thread Directory.Build.props
Chris-Wolfgang and others added 3 commits May 22, 2026 21:05
Adds queries: security-extended to the CodeQL init step so the broader
security query pack runs on top of the default queries. Slightly longer
scans, materially more security coverage.

Initiative S1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New .github/workflows/stryker.yaml runs Stryker.NET against the repo's
test projects on workflow_dispatch and a weekly schedule. The workflow
is a no-op until a stryker-config.json is added at repo root or under
tests/<project>/ — this commit is the canonical infrastructure;
per-repo Stryker config is the follow-up.

Initiative T3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a verify-docs-build job to release.yaml that runs DocFX without
deploying (metadata + build + output check). publish-nuget now needs
[pack-and-validate, verify-docs-build] so a broken docs build blocks
the release before the NuGet package goes live.

If a repo has no docfx_project/docfx.json, the job no-ops with a
notice; this is the canonical infrastructure, with per-repo docs
coverage tracked separately.

Initiative D8.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 23, 2026 01:18
@Chris-Wolfgang

Copy link
Copy Markdown
Owner Author

Scope additions to this PR — S1 + D8 stacked.

The canonical-protected branch now also carries:

  • S1queries: security-extended added to .github/workflows/codeql.yaml (3-line addition under the CodeQL init step) for broader security-query coverage.
  • D8 — new verify-docs-build job in .github/workflows/release.yaml; publish-nuget now needs: [pack-and-validate, verify-docs-build] so a docs failure blocks the release before the NuGet package goes live.

All four logical changes — C1 drift re-sync, nullable consolidation, S1, D8 — ride this single protected PR per the standing-branch model and merge atomically via admin bypass.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 8 comments.

Comment thread .github/workflows/pr.yaml
Comment thread .github/workflows/pr.yaml
Comment thread .github/workflows/pr.yaml
Comment thread .editorconfig
Comment thread BannedSymbols.txt Outdated
Comment thread .github/workflows/docfx.yaml
Comment thread .github/workflows/docfx.yaml
Comment thread Directory.Build.props Outdated
Add a guard step to docfx.yaml that fetches the currently-deployed
versions.json from gh-pages and confirms the newly-generated one has
at least as many entries AND retains every previously-published version
label. Aborts the deploy if the version selector would shrink or lose
entries.

If no existing versions.json is found (first deploy), the step no-ops
with a notice.

Initiative D6.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Chris-Wolfgang

Copy link
Copy Markdown
Owner Author

Scope addition to this PR — D6 stacked.

Adds a Verify previous versions preserved step to .github/workflows/docfx.yaml (right before Compute destination directory). The step fetches the currently-deployed versions.json from gh-pages and aborts the deploy if the newly-generated file would shrink or drop any previously-published version label — guards against accidentally wiping the docs version selector.

Add the Microsoft.CodeAnalysis.PublicApiAnalyzers package plus opt-in
AdditionalFiles globbing for PublicAPI.Shipped.txt / Unshipped.txt to
the root Directory.Build.props.

The AdditionalFiles use Exists() conditions, so the analyzer activates
per-project only when those files are present. Library projects opt in
by dropping the two text files into the src directory; test, example,
and benchmark projects stay dormant.

Per-repo enablement (populate Unshipped.txt with the current public API
surface) is tracked as a separate follow-up maintenance issue.

Initiative A1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 23, 2026 18:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated 10 comments.

Comment thread .editorconfig
Comment thread BannedSymbols.txt Outdated
Comment thread Directory.Build.props
Comment thread .github/workflows/pr.yaml
Comment thread .github/workflows/pr.yaml
Comment thread .github/workflows/pr.yaml
Comment thread .github/workflows/pr.yaml
Comment thread .github/workflows/pr.yaml
Comment thread .github/workflows/pr.yaml
Comment thread .github/workflows/pr.yaml
Chris-Wolfgang and others added 2 commits May 23, 2026 15:19
Add fleet-canonical defaults to root Directory.Build.props:

- Authors / Company / Copyright (uniform across the fleet; per-csproj
  values still win where set explicitly).
- RepositoryType=git, PublishRepositoryUrl=true.
- IncludeSymbols=true + SymbolPackageFormat=snupkg so .snupkg ships
  with every .nupkg.
- EmbedUntrackedSources=true to capture generated sources in PDBs.
- ContinuousIntegrationBuild=true under $(CI)=true (deterministic
  build flag, set by GitHub Actions).
- Microsoft.SourceLink.GitHub package so debuggers can step from
  NuGet-installed code straight to GitHub source.

Repo-specific NuGet fields (Description, PackageTags, PackageProjectUrl,
RepositoryUrl, PackageLicenseExpression, PackageReadmeFile) stay in
per-src csproj where they belong and are tracked as per-repo follow-up
maintenance issues.

Initiative CI3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a docfx.yaml step that runs the test suite with Cobertura coverage
collection and generates a ReportGenerator HTML report into
docfx_project/_site/coverage/ before the deploy step. The published
docs site gains a /coverage/ subpath alongside the existing /api/.

continue-on-error keeps a coverage failure from blocking the docs
deploy; if no test projects are present the step no-ops.

Initiative T1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 23, 2026 19:22
Copilot AI review requested due to automatic review settings May 25, 2026 19:36
Copy-Item -Force overwrites files that exist in both source and
destination, but doesn't remove destination-only files. Over multiple
releases — if a docs page or asset is dropped from docfx output —
the stale file lingers indefinitely in gh-pages, served alongside
the current docs.

Adding a clear-before-copy step for both versions/<VERSION_DIR> and
versions/latest. Preserves the directory entry itself so per-release
git diffs stay clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated no new comments.

Chris-Wolfgang and others added 2 commits May 25, 2026 15:40
The earlier "Restore dependencies" step already does the restore, so
the implicit restore inside dotnet test is wasted I/O. Adding
--no-restore alongside the existing --no-build to skip both.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The D6 "Verify previous versions preserved in versions.json" guard
ran whenever deploy_to_pages != false. But the deploy step only
touches the root versions.json when deploy_as_latest is ALSO true:

  - deploy_to_pages=false → dry-run, nothing deploys
  - deploy_to_pages=true + deploy_as_latest=false → rebuild a single
    older version; deploy writes only versions/<dir>/, doesn't touch
    the root
  - deploy_to_pages=true + deploy_as_latest=true → full deploy that
    overwrites the root versions.json

The guard exists to protect that root file. In the rebuild-an-older-
version case there's nothing for the guard to protect, but it still
fetched the live Pages versions.json and compared — meaning a
transient Pages fetch/parse error would block a legitimate rebuild.

Tightening the if condition so the guard only runs in the third case
(both inputs true).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 25, 2026 19:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated 2 comments.

Comment thread .github/workflows/pr.yaml
Comment thread .github/workflows/pr.yaml
Chris-Wolfgang and others added 2 commits May 25, 2026 15:55
Write-Error prefixes its output with PowerShell error formatting
(file/line/category metadata), so a literal ::error::... payload
no longer starts the line — and Actions' workflow-command parser
looks for ::error:: at the BEGINNING of a stream line. The result
was that the missing-versions.json failure produced a red error in
the log but no annotation marker on the run summary.

Switching to Write-Host (literal output, no PowerShell prefixing)
plus the existing exit 1 keeps the annotation visible and still
fails the step.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
My earlier Stage 1 hardening kept the integer-only regex [0-9]+% and
the bash integer comparator -lt. ReportGenerator typically emits
decimals like "92.3%" — the regex missed those, and the new
matched_count=0 guard would then fail jobs whose Summary.txt had only
decimal rows. Even after broadening the regex to accept decimals, bash
[ errors with "integer expression expected" on a non-integer value.

Two fixes:

  1. Regex now matches [0-9]+(\.[0-9]+)?% so decimal percents
     count toward matched_count.

  2. Percent is floored to an integer via awk '{print int($1)}'
     before the -lt comparison. Matches Stage 2 pwsh's
     [int][math]::Floor([double]$Matches[2]) semantically.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 25, 2026 19:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated 1 comment.

Comment thread .github/workflows/docfx.yaml
The Stage 2 (Windows) coverage gate regex

    ^\s*(\S+)\s+.*(\d+(?:\.\d+)?)%\s*$

had a greedy `.*` between the module name and the trailing `\d+%`,
which ate all but the last digit of the percent. On lines like

    Wolfgang.Extensions.<X>                         100%

the regex captured percent=0 (the last "0" of "100"), reported the
module as failing the 90% threshold, and tanked the entire gate even
on 100%-covered code. First surfaced on DateTime-Extensions vNext —
PR #189 has the original fix.

Two changes to align with Stage 1 (Linux), whose awk-based parser is
correct:

- Anchor on `^(\S+)` to skip indented sub-class rows (Stage 1's
  `^[^ ]` does the same — assembly rows carry the aggregate
  percent, so nothing is lost).

- Drop the `.*`; let `\s+` separate the module from the final
  `\d+%` directly, so there is no greedy region to swallow digits.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dependabot Bot added 3 commits May 27, 2026 22:33
---
updated-dependencies:
- dependency-name: coverlet.collector
  dependency-version: 10.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
---
updated-dependencies:
- dependency-name: Meziantou.Analyzer
  dependency-version: 3.0.93
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
---
updated-dependencies:
- dependency-name: SonarAnalyzer.CSharp
  dependency-version: 10.26.0.140279
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Copilot AI review requested due to automatic review settings May 28, 2026 02:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated 6 comments.

Comment thread .github/workflows/pr.yaml
Comment thread .github/workflows/pr.yaml
Comment thread .github/workflows/pr.yaml
Comment thread .github/workflows/pr.yaml
Comment thread .github/workflows/pr.yaml
Comment thread .github/workflows/pr.yaml
@Chris-Wolfgang

Copy link
Copy Markdown
Owner Author

Triage summary on the Copilot review threads

Verified each concern against the actual state on canonical-protected. All 18 unresolved threads are either stale (Copilot misread the tree) or cosmetic. Resolving them in batch.

Stale — coverlet.runsettings claimed missing (12 threads)

Copilot says dotnet test --settings coverlet.runsettings will fail because the file doesn't exist in the repo. Verified the file IS present on canonical-protected:

$ gh api repos/.../contents/coverlet.runsettings?ref=canonical-protected --jq .name
coverlet.runsettings

No bug. The pr.yaml flag matches a real file. The 12 threads on lines 530 / 707 / 821 / 1044 / 1227 (×3 each across Linux/Windows/macOS stages, with duplicates across review passes) all resolve as stale-versus-tree.

Stale — BannedSymbols.txt {{PROJECT_NAME}} placeholder (5 threads)

Copilot says the header was changed to the template placeholder. Verified the actual header on canonical-protected:

# BannedSymbols.txt - Async-First Enforcement for Wolfgang.Extensions.IEnumerable

The header correctly names this library. No placeholder present. Copilot was looking at a different state.

Cosmetic — PR description vs scope (1 thread)

Copilot flags that the PR description says "5 protected files" but the PR also modifies Directory.Build.props, multiple csprojs, and release.yaml. This is a description accuracy concern, not a content bug. The actual content is the intended canonical-protected drift; the description was a generic template-style summary that could be more precise. Not blocking the merge.

Out-of-band concerns (no action needed for this PR)

A few comments raised improvements that are real but not blocking:

  • .github/workflows/docfx.yaml uses exit 1 inside try {} blocks — could theoretically skip the finally cleanup in some PowerShell hosts, risking a token-config leak across steps. Improvement-level (the runner is destroyed after the workflow anyway). Will be a fleet-wide canonical-workflow follow-up rather than blocking this release.
  • .editorconfig removed [*.ps1] override vs .gitattributes enforcing CRLF — already addressed in the companion canonical-unprotected work that's now in vNext (.gitattributes enforces LF, matching the editorconfig removal).
  • Directory.Build.props PublicApiAnalyzers PackageReference is unconditional — by design. The analyzer 3.3.4 self-disables when PublicAPI.*.txt files are absent; opt-in is via <AdditionalFiles ... Condition="Exists(...)">. Comment in the file documents this.

Resolving all 18 threads.

The workflow consumes coverlet.runsettings via dotnet test --settings
coverlet.runsettings in all three test stages (Linux/Windows/macOS), but
the file was not in any of the trusted-config protection lists:

1. detect-projects exact_files (guard) — a PR could modify
   coverlet.runsettings without triggering the maintainer-review gate.
2. The four fetch-from-main lists (one per test/scan job) — under
   pull_request_target, the workflow YAML is trusted but the checkout
   uses the PR head. Without fetching coverlet.runsettings from main,
   the PR-controlled version is what dotnet test reads.

A PR could add <Exclude>**/*</Exclude> to coverlet.runsettings to
exclude all source files from coverage, then everything that does run
through the parser shows 100% coverage (or the matched-modules guard
fires, depending on parser layout). Either way the coverage gate is
weakened.

Adding coverlet.runsettings to all 6 lists (5 fetch + 1 exact_files
guard) closes the gap. The file is present in this repo and will be
fetched from main in every CI job; PRs that modify it will now also
trigger the maintainer-review path.

Surfaced by Copilot review on PR #135. Fleet-wide canonical workflow
fix — the next template-sync wave fans this out to the other 25 repos.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Chris-Wolfgang

Copy link
Copy Markdown
Owner Author

Triage on the 7 newer unresolved threads (page-2 catch)

Stray YAML block claim (1 thread, 2026-05-25)

Stale. The workflow has been running on every PR (it parses fine). Examined the file end-to-end: security-scan job ends cleanly at the last upload-artifact step; no orphan content after it. The pattern Copilot pointed at (-and \$line -notmatch ...) is legitimate PowerShell inside the Stage 2 coverage parser. Resolving.

coverlet.runsettings should be a trusted/protected config (6 threads, 2026-05-28)

Real concern — fixed in 96ba9df. The workflow consumes coverlet.runsettings via dotnet test --settings coverlet.runsettings in all three test stages, but the file wasn't in any of the trusted-config protection lists. Under pull_request_target, a PR could modify it to weaken the coverage gate (e.g., <Exclude>**/*</Exclude> would either zero out coverage or trip the matched-modules guard).

Added "coverlet.runsettings" to all 6 lists in pr.yaml:

  • exact_files guard (detect-projects job)
  • 4× bash config_files fetch lists (Linux test, macOS test, Stage 1 setup, security-scan)
  • 1× PowerShell $configFiles fetch list (Windows test)

After this change:

  • PRs that modify coverlet.runsettings trigger the maintainer-review gate.
  • All test/scan jobs fetch the trusted version from main before running tests, so PR-controlled runsettings no longer affect coverage collection.

Fleet-wide canonical fix — propagates to the other 25 repos via the next template-sync wave.

Resolving all 7.

@Chris-Wolfgang Chris-Wolfgang merged commit ed4d9e8 into main May 29, 2026
7 of 8 checks passed
@Chris-Wolfgang Chris-Wolfgang deleted the canonical-protected branch May 29, 2026 18:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment