From 097a2742dacb5e91af4beba186b8b068b3e95b3f Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Thu, 13 Aug 2026 16:35:50 -0700 Subject: [PATCH 1/3] Condition PSScriptAnalyzer CI Claim on Repos Carrying .ps1 Files GOVERNANCE.md's Running the Linters Locally bullet unconditionally claimed CI runs PSScriptAnalyzer and linked PSScriptAnalyzerSettings.psd1, true for the hub itself but not for every downstream repo. Hit as a real Copilot finding on both Blog#94 (a stale carried validate-task.yml never wired the step) and HomeAutomation-Config#50 (the repo carries no .ps1 files at all, so the link 404s). Neither is fixable per-repo without either breaking byte-parity on the verbatim bullet or inventing content the repo does not need, so both were replied-and-deferred as the maintainer's call. Condition both bullets on a repo carrying .ps1 files instead, closing the recurring gap at its source. Co-Authored-By: Claude Sonnet 5 --- GOVERNANCE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 75fc9b9..1c3dce3 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -203,7 +203,7 @@ CI runs the full lint set, but run the linters locally before pushing to catch i **Each surface runs the lint with the tool that fits it, all from the same config files** (`.markdownlint-cli2.jsonc`, `cspell.json`, `.editorconfig`): -- **CI (authoritative)** runs **markdownlint-cli2**, **cspell**, and **actionlint** as pinned action wrappers (Dependabot bumps them), plus **editorconfig-checker** and **PSScriptAnalyzer** via Docker `:latest` (editorconfig-checker's action only installs the CLI, and PSScriptAnalyzer has no action, so the Docker one-liner is what actually runs each check). markdownlint covers all `**/*.md`, and **cspell is scoped to `README.md` + `HISTORY.md`** (see [CODESTYLE.md](./CODESTYLE.md) "Markdown and Spelling" for why), matching the cspell one-liner below. +- **CI (authoritative)** runs **markdownlint-cli2**, **cspell**, and **actionlint** as pinned action wrappers (Dependabot bumps them), plus **editorconfig-checker** via Docker `:latest` (its action only installs the CLI, so the Docker one-liner is what actually runs the check), and, **for a repo that carries `.ps1` files**, **PSScriptAnalyzer** the same way (it has no action either). markdownlint covers all `**/*.md`, and **cspell is scoped to `README.md` + `HISTORY.md`** (see [CODESTYLE.md](./CODESTYLE.md) "Markdown and Spelling" for why), matching the cspell one-liner below. - **The `.husky/pre-commit` hook** runs **language formatting** and the **diff-scoped doc gates**, never Docker and never a network call, so it stays fast. The formatting half is whatever the repo's own language needs, CSharpier and `dotnet format` for .NET or ruff for Python, via native tooling. A repo adds each half once its tree passes that half, since a gate that fails on the corpus it guards blocks every commit from the moment it lands, so a hook running one half is a repo mid-convergence rather than a repo out of conformance. The doc half runs each gate at the scope that fits it. The prose gate is scoped to what the commit changes rather than swept over the tree, which is the difference between about 2.2 seconds and about 0.13 and is what makes it affordable in a hook at all. A whole-repo check belongs there too when it is already fast and takes no file list, which the line-ending consistency check is, so scope is a property of the gate rather than a rule the hook applies to all of them. `repo_gate.py --check sha-pin` stays out, since it resolves a same-owner pin against the GitHub API and a hook that needs a network fails offline. A repo enables the hook per clone with `git config core.hooksPath .husky`, and CI remains the authoritative run either way. - **The VS Code Lint tasks** run the full doc-lint set via Docker `:latest` on demand, the local surface for Markdown, spelling, workflow, and EditorConfig checks. @@ -235,7 +235,7 @@ The Docker invocations below are the same ones the VS Code tasks use, for ad-hoc docker run --rm --pull=always -v "$PWD":/workdir --workdir /workdir ghcr.io/streetsidesoftware/cspell:latest --no-progress README.md HISTORY.md ``` -- **PSScriptAnalyzer** (PowerShell, the peer of the shellcheck step, with the excluded rules and their reasons in [`PSScriptAnalyzerSettings.psd1`](./PSScriptAnalyzerSettings.psd1)): +- **PSScriptAnalyzer** (PowerShell, the peer of the shellcheck step, **only applies to a repo that carries `.ps1` files**, with the excluded rules and their reasons in [`PSScriptAnalyzerSettings.psd1`](./PSScriptAnalyzerSettings.psd1) for a repo that carries one): ```sh docker run --rm --pull=always -e PS_SCRIPTS="$(git ls-files '*.ps1')" -v "$PWD":/mnt --workdir /mnt mcr.microsoft.com/powershell:latest \ From 62f5b509e054c92993d2095fdb8baacbc682b5e5 Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Thu, 13 Aug 2026 16:38:27 -0700 Subject: [PATCH 2/3] Drop the 404-Risking Hyperlink on PSScriptAnalyzerSettings.psd1 Round 1 review: the surrounding prose became conditional on a repo carrying .ps1 files, but the Markdown hyperlink to PSScriptAnalyzerSettings.psd1 stayed unconditional and still 404s when carried into a repo without the file. Switch to inline code, no link. Co-Authored-By: Claude Sonnet 5 --- GOVERNANCE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 1c3dce3..19e6e78 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -235,7 +235,7 @@ The Docker invocations below are the same ones the VS Code tasks use, for ad-hoc docker run --rm --pull=always -v "$PWD":/workdir --workdir /workdir ghcr.io/streetsidesoftware/cspell:latest --no-progress README.md HISTORY.md ``` -- **PSScriptAnalyzer** (PowerShell, the peer of the shellcheck step, **only applies to a repo that carries `.ps1` files**, with the excluded rules and their reasons in [`PSScriptAnalyzerSettings.psd1`](./PSScriptAnalyzerSettings.psd1) for a repo that carries one): +- **PSScriptAnalyzer** (PowerShell, the peer of the shellcheck step, **only applies to a repo that carries `.ps1` files**, with the excluded rules and their reasons in `PSScriptAnalyzerSettings.psd1` alongside a repo that carries one): ```sh docker run --rm --pull=always -e PS_SCRIPTS="$(git ls-files '*.ps1')" -v "$PWD":/mnt --workdir /mnt mcr.microsoft.com/powershell:latest \ From a914a521495c1473ea51a05fc0336969dc23324c Mon Sep 17 00:00:00 2001 From: Pieter Viljoen Date: Thu, 13 Aug 2026 16:42:08 -0700 Subject: [PATCH 3/3] State That a .ps1-Carrying Repo Also Carries the Settings File Round 2 review (suppressed): the invocation still unconditionally passed -Settings ./PSScriptAnalyzerSettings.psd1, but nothing said a repo carrying .ps1 files also carries that settings file, so a repo with scripts but no settings file would hit an immediate hard failure. State the pairing explicitly rather than adding fallback logic the rest of this doc's required-baseline-file pattern does not use elsewhere. Co-Authored-By: Claude Sonnet 5 --- GOVERNANCE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 19e6e78..7a2c258 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -235,7 +235,7 @@ The Docker invocations below are the same ones the VS Code tasks use, for ad-hoc docker run --rm --pull=always -v "$PWD":/workdir --workdir /workdir ghcr.io/streetsidesoftware/cspell:latest --no-progress README.md HISTORY.md ``` -- **PSScriptAnalyzer** (PowerShell, the peer of the shellcheck step, **only applies to a repo that carries `.ps1` files**, with the excluded rules and their reasons in `PSScriptAnalyzerSettings.psd1` alongside a repo that carries one): +- **PSScriptAnalyzer** (PowerShell, the peer of the shellcheck step, **only applies to a repo that carries `.ps1` files**, which carries `PSScriptAnalyzerSettings.psd1` alongside them with the excluded rules and their reasons): ```sh docker run --rm --pull=always -e PS_SCRIPTS="$(git ls-files '*.ps1')" -v "$PWD":/mnt --workdir /mnt mcr.microsoft.com/powershell:latest \