CI: stop charging documentation changes for a .NET restore - #1712
Merged
Conversation
Non-executable changes now skip .NET setup, restore and versioning in the required build job. The job still RUNS, so the check reports a result and cannot go missing and block a merge. The gate is an explicit ALLOWLIST of non-executable content, not a subtraction of known-code paths: **/*.md, LICENSE, CITATION.cff, .gitignore, .gitattributes, docs/**, Screenshots/**. An unfamiliar new file type therefore defaults to being treated as code, which is the safe direction to be wrong in. Deliberately NOT in the allowlist: *.sql (the installer and sql-validation compile it), *.yml (workflows), *.csproj / *.props / packages.lock.json (build inputs), and *.cs however comment-only a change looks - an XML doc comment still recompiles, and the compiler is what proves it still builds. Two guards against silently under-building: - The fast path never engages on a release event, or on a push to dev/main. Those restore unconditionally. Note this only forces the RESTORE back on; the per-product build/test steps stay path-gated exactly as before, so push times are unchanged for code commits. - Both jobs emit a ::notice:: naming why they took the path they took, and the build job lists the files it classified as documentation. A job reporting success having quietly run nothing is otherwise indistinguishable from one that tested everything. On what this actually fixes: markdown-only changes were ALREADY skipping every heavy step - #1707 was pure .md and its build ran in 1m43s, which is checkout and the paths filter with nothing after it. The real gap was non-markdown documentation, which matched the '**' catch-all in the code filter and paid a six-project locked-mode restore to compile nothing. The remaining ~1m45s floor is actions/checkout on a Windows runner and is not something this gate can remove. The docs allowlist and the code filter's exclusions are the same list stated twice - paths-filter has no "not in that other filter" operator - so they carry a comment to keep them in step. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
erikdarlingdata
enabled auto-merge
July 26, 2026 20:22
# Conflicts: # CHANGELOG.md
erikdarlingdata
disabled auto-merge
July 26, 2026 20:24
erikdarlingdata
enabled auto-merge
July 26, 2026 20:24
pull Bot
pushed a commit
to ehtick/PerformanceMonitor
that referenced
this pull request
Jul 29, 2026
…rrors it Ratifies erikdarlingdata#1712's two flagged judgment calls (CITATION.cff and the Screenshots/ entry stay on the documentation allowlist - citation metadata and screenshot images cannot affect any build.yml job) but replaces the bare directory globs docs/** and Screenshots/** with extension-explicit patterns for markdown, SVG, and image files. Both directories hold exactly those types today, so nothing classified as documentation before this commit stops being documentation - but a .sql or script dropped into either directory tomorrow now defaults to being CODE, which restores the allowlist's own stated principle (an unfamiliar file type should default to the safe direction) that a directory-wide grant quietly undermined. check-version-bump.yml gets the same allowlist as exclusions: its gate still said *.md-only, so a LICENSE or .gitignore correction in a dev to main PR would demand a version bump while build.yml calls the same diff documentation. The two filters now state the same policy and say so. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pull Bot
pushed a commit
to ehtick/PerformanceMonitor
that referenced
this pull request
Jul 29, 2026
The 2026-07-26 08:02 action-pin bump moved dorny/paths-filter v3 to v4.
v4 evaluates every filter pattern as an INDEPENDENT predicate under the
default predicate-quantifier 'some' (a filter is true when any changed
file matches at least one rule), so a bare '!**/*.md' line stopped
being a subtraction and became its own rule: 'any file that is not
markdown'. Every area filter ending in that line went true for ANY
non-markdown change anywhere in the repo, and the code: filter ('**'
plus exclusions) could never be false again.
Measured consequences, straight from run logs:
- darling-pg ran the full TimescaleDB suite on every PR since the bump,
including md-only ones. Run 30218459544 (PR erikdarlingdata#1709, two .md files):
'Filter darling = true, Matching files: CHANGELOG.md' - CHANGELOG.md
matched '!Darling/**/*.md' by virtue of not being a Darling markdown.
- A single root .gitignore edit built and tested every product: run
30219202642 (throwaway PR erikdarlingdata#1714) lit lite, dashboard, AND darling,
each reporting 'Matching files: .gitignore' via its '!**/*.md' rule.
- The erikdarlingdata#1712 docs fast path shipped unable to engage: every changed file
matches '**', so code: was always true and setup/restore always ran.
Its measured 1m43s md-only runs were real but came from the area
filters (markdown matches no include), not from the fast path.
The fix keeps v4 (v3 is on the deprecated-runtime track) and removes
every bare negation instead:
- Area filters state the markdown carve-out INSIDE each include as an
extglob - 'Darling/**/!(*.md)' - where quantifier semantics cannot
detach it. The positive-only docs allowlist needed no change.
- The uninvertible code: filter becomes an all: counter. The classify
step decides docs-only by all_count == docs_count, and additionally
refuses to engage while any area filter is lit, because an area build
step with restore skipped would run dotnet build --no-restore against
nothing. Counts and areas derive from the same allowlist today; the
veto exists for the day someone edits one and not the other.
- check-version-bump.yml had the identical '**'-plus-exclusions shape,
so its md-only skip had also silently stopped working. Same counter
fix, same allowlist, and its gate now says why.
Validation: probe commit on throwaway PR erikdarlingdata#1714 (this build.yml plus a
Darling .txt probe and a Darling .md probe) makes the filter log a
per-file truth table; results recorded in the CI-intelligence PR body.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Makes non-executable changes stop paying for a .NET restore in the required
buildjob, while keeping the check reporting so it can never go missing and block a merge.First, a correction to the brief's premise
The brief said the
rootcatch-all dragsCHANGELOG.mdinto full builds. It does not —rootis four specific files (PerformanceMonitor.sln,global.json,nuget.config,.github/workflows/build.yml) and no glob there matches a.mdfile. I checked before changing it, sorootis untouched.I also checked the claim empirically rather than by reading, and markdown-only changes were already fast-pathing correctly:
buildCHANGELOG.md,tools/ag-fixture/VALIDATION.md— pure.mdCHANGELOG.md+DarlingSelfAlertEvaluator.cs#1708 looked like a docs PR and is what makes the problem seem worse than it is — it contains a
.csfile, so its 6m58s was a legitimate build. #1707 is the true control: 1m43s, which isactions/checkoutplus the paths filter and nothing after it. The existingcode: ['**', '!**/*.md']filter was already doing its job.The real gap is non-markdown documentation.
LICENSE,CITATION.cff,.gitignore,.gitattributes,docs/signpath_logo.svg, and everything underScreenshots/are not.md, so they matched the**catch-all, setcode=true, and paid a six-project--locked-moderestore to compile nothing. That is what this PR fixes.The gate
An explicit allowlist of non-executable content, not a subtraction of known-code paths:
Allowlist direction matters: an unfamiliar new file type defaults to being treated as code, which is the safe way to be wrong. A subtraction would default new file types to "documentation" and silently stop building them.
Deliberately not in the allowlist, per the brief and verified against what each job consumes:
*.sql(the installer andsql-validation.ymlcompile it),*.yml(workflows),*.csproj/*.props/packages.lock.json(build inputs), and*.cshowever comment-only a change looks — an XML doc comment still recompiles, and the compiler is what proves it still builds.Screenshots/**andCITATION.cffare additions beyond the brief's list. Both are unambiguously non-executable and both are real categories in this repo (Screenshots/is PNGs;CITATION.cffis citation metadata). Flagging them explicitly since they were my call, and either is a one-line removal.Two guards against silently under-building
Never on
release, never on a push todev/main. Those restore unconditionally. Worth being precise about the scope: this only forces the restore back on — the per-product build/test/publish steps stay path-gated exactly as they were, so push times for code commits are unchanged. I did not make dev pushes rebuild everything; that would have been a much larger behavioural change than this job asked for, and it would slow every push.Both jobs now say what they did and why. The
buildjob emits a::notice::naming the reason (release / branch push / docs-only / code changed) and, on the fast path, lists every file it classified as documentation vialist-files: shell. TheDarling PostgreSQL testsjob gets the same treatment — its gate was already correct for documentation, but a job reporting success having quietly run nothing is indistinguishable from one that tested everything, and now it isn't.Scope notes
darling-pg's gate is unchanged — it was already correct (Darling/**minusDarling/**/*.md), so a docs-only change already no-ops every step. It gained only the reporting step. Note its filter deliberately includes.github/workflows/build.yml, which is why this PR runs the full TimescaleDB suite against itself rather than skipping.sql-validation.ymluntouched — not a required check and already paths-scoped, as specified.codefilter's exclusions are the same list stated twice, because paths-filter has no "not in that other filter" operator. They carry a comment to keep them in step, and I added a check that they mirror exactly.Validation
Per step 4 of the brief, this PR is itself code (
.yml), so it correctly takes the full path — including the Darling PG suite, sincebuild.ymlis in that job's filter. After it merges I'll open a throwaway docs-only PR (a one-sentence README edit), confirm thebuildcheck completes fast and green with the fast-path notice visible in its log, post the timing as a comment here, and close the throwaway.🤖 Generated with Claude Code