From 8e1c49d927ad02d30c4e600fdb5ba669a57e4423 Mon Sep 17 00:00:00 2001
From: Pieter Viljoen <ptr727@users.noreply.github.com>
Date: Mon, 22 Jun 2026 09:55:53 -0700
Subject: [PATCH 1/2] Re-sync from template: consolidate CODESTYLE, tasks,
 smoke skip

Applies the three template changes from issue #176's 2026-06-22
comment (upstream cde3354/f526b55/402e862):

- copilot-instructions.md: re-sync the hardened Copilot review runbook
  verbatim (clean-pass-is-success, round-1 auto-seed polling, REST vs
  GraphQL bot-login, issue-comment bot-id fallback); re-adapt owner/repo
  placeholders and drop the inapplicable Python pointer.
- CODESTYLE.md: consolidate into one guide - General + .NET sections,
  Python section dropped (droppable per-language model); adapt the
  project list, InternalsVisibleTo name, task labels, and Husky.Net note.
- .vscode/tasks.json, launch.json, .husky/task-runner.json: .Net -> .NET
  casing; add dependsOrder: sequence to the .NET Format task.
- build-nugetlibrary-task.yml: add smoke input, gate the zip/upload on
  !inputs.smoke; build-release-task.yml forwards smoke to the call.
- .editorconfig: Json/JsonC comment and the suppression-policy note.
- AGENTS.md: update CODESTYLE re-sync guidance to the droppable-section
  model and add the tasks.json verbatim-carry note.

Build: 0 warnings / 0 errors. Tests: 257/257 pass.
---
 .editorconfig                                 |   5 +-
 .github/copilot-instructions.md               |  14 +-
 .github/workflows/build-nugetlibrary-task.yml |   9 +
 .github/workflows/build-release-task.yml      |   2 +
 .husky/task-runner.json                       |   2 +-
 .vscode/launch.json                           |   2 +-
 .vscode/tasks.json                            |  15 +-
 AGENTS.md                                     |   3 +-
 CODESTYLE.md                                  | 158 +++++++++++-------
 9 files changed, 133 insertions(+), 77 deletions(-)

diff --git a/.editorconfig b/.editorconfig
index f0d75d2..23c9b7c 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -36,7 +36,7 @@ indent_size = 2
 end_of_line = crlf
 indent_size = 2
 
-# Json files
+# Json and JsonC files
 [*.{json,jsonc}]
 end_of_line = crlf
 
@@ -54,6 +54,9 @@ end_of_line = crlf
 # C# files
 [*.cs]
 end_of_line = crlf
+# Suppressions follow CODESTYLE.md "Analyzer Diagnostics and Suppressions": prefer a
+# [SuppressMessage] attribute or the owning project's .editorconfig; relax a rule
+# repo-wide here only when it applies to every project (never a brownfield batch).
 dotnet_diagnostic.IDE0055.severity = none
 dotnet_analyzer_diagnostic.severity = suggestion
 csharp_indent_block_contents = true
diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
index 900529a..a228ea4 100644
--- a/.github/copilot-instructions.md
+++ b/.github/copilot-instructions.md
@@ -1,8 +1,12 @@
-# GitHub Copilot Instructions for LanguageTags
+# Copilot Instructions
+
+Repository conventions for GitHub Copilot (and any other AI agent reading this file).
 
 The **canonical guide is [AGENTS.md](../AGENTS.md)** at the repo root - read it first, including the [PR Review Etiquette](../AGENTS.md#pr-review-etiquette) review-loop contract this file's runbook implements. This file is intentionally narrow: commit/PR-title conventions (summarized inline so VS Code's commit-message and PR-title generators have them) plus the GitHub Copilot Review Runbook.
 
-For C# style rules, see [`CODESTYLE.md`](../CODESTYLE.md) at the repo root. Do not duplicate those rules here. **Project-specific conventions and API/behavioral contracts also belong in [AGENTS.md](../AGENTS.md), not here** - this file is intentionally limited to the inline commit/PR-title summary and the GitHub Copilot Review Runbook. Non-Copilot agents (Claude Code, Codex, Cursor, ...) are not directed to this file and don't read it by default, so any rule a reviewer must honor has to live in `AGENTS.md` to be provider-independent.
+For code-style rules, see [`CODESTYLE.md`](../CODESTYLE.md) at the repo root - one guide with a General section plus the .NET language section.
+
+Do not duplicate language-specific rules here. **Project-specific conventions and API/behavioral contracts also belong in [AGENTS.md](../AGENTS.md), not here** - this file is intentionally limited to the inline commit/PR-title summary and the GitHub Copilot Review Runbook. Non-Copilot agents (Claude Code, Codex, Cursor, ...) are not directed to this file and don't read it by default, so any rule a reviewer must honor has to live in `AGENTS.md` to be provider-independent.
 
 ## Commit Messages and Pull Request Titles
 
@@ -138,11 +142,13 @@ Issue-level Copilot comments (those in `issues/<N>/comments`) have no resolution
 Reply-body conventions:
 
 - Accepted bug/style fix: include fixing commit SHA and a one-line summary.
-- Declined style comment: cite the rule (AGENTS.md or CODESTYLE.md) and the existing-tree precedent.
+- Declined style comment: cite the rule (AGENTS.md or the CODESTYLE.md language section) and the existing-tree precedent.
 - Declined architecture proposal: one-sentence rationale.
 
 After the final push, sweep-resolve stale older threads for removed code paths.
 
 ## When in Doubt
 
-Read [AGENTS.md](../AGENTS.md) for this repo's conventions; [`CODESTYLE.md`](../CODESTYLE.md) is authoritative for C# style. Don't restate any of these files' rules in commit bodies or PR descriptions - keep those focused on the change itself. If you find a discrepancy that should be fixed in the template itself (this file or AGENTS.md is out of date, a rule is missing, something bit this repo and would bite the next derived repo), open an issue upstream in [`ptr727/ProjectTemplate`](https://github.com/ptr727/ProjectTemplate) rather than only fixing it locally - see [AGENTS.md "Staying in Sync with the Template"](../AGENTS.md#staying-in-sync-with-the-template).
+Read [AGENTS.md](../AGENTS.md) for this repo's conventions. For code-style rules, [`CODESTYLE.md`](../CODESTYLE.md) (its General section plus the relevant language section) is authoritative. Don't restate any of these files' rules in commit bodies or PR descriptions - keep those focused on the change itself.
+
+**In a derived repo:** if you find a discrepancy that should be fixed in the template itself (this file or AGENTS.md is out of date, a rule is missing, something bit this repo and would bite the next), open an issue upstream in [`ptr727/ProjectTemplate`](https://github.com/ptr727/ProjectTemplate) rather than only fixing it locally - see the template's [AGENTS.md "Staying in Sync and Reporting Drift Upstream"](https://github.com/ptr727/ProjectTemplate/blob/main/AGENTS.md#staying-in-sync-and-reporting-drift-upstream).
diff --git a/.github/workflows/build-nugetlibrary-task.yml b/.github/workflows/build-nugetlibrary-task.yml
index 5dd96c2..ee4879a 100644
--- a/.github/workflows/build-nugetlibrary-task.yml
+++ b/.github/workflows/build-nugetlibrary-task.yml
@@ -22,6 +22,12 @@ on:
       branch:
         required: true
         type: string
+      # Smoke mode: build for validation only and skip the artifact zip/upload. A PR smoke run has no consumer for
+      # the artifact (the github-release job is gated `!smoke`), so uploading it just burns artifact storage.
+      smoke:
+        required: false
+        type: boolean
+        default: false
     outputs:
       # Output of the uploaded artifact id
       artifact-id:
@@ -78,13 +84,16 @@ jobs:
             --skip-duplicate
 
       - name: Zip output step
+        if: ${{ !inputs.smoke }}
         run: |
           set -euo pipefail
           7z a -t7z ${{ runner.temp }}/${{ env.PROJECT_ARTIFACT }} ${{ runner.temp }}/publish/*
 
       # Branch-suffixed so the publisher's branch matrix can build both
       # branches in one run without colliding on the artifact name.
+      # Skipped on smoke: the github-release job is `!smoke`, so nothing would consume it.
       - name: Upload build artifacts step
+        if: ${{ !inputs.smoke }}
         id: artifact-upload-step
         uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
         with:
diff --git a/.github/workflows/build-release-task.yml b/.github/workflows/build-release-task.yml
index 0c8b291..17acb5a 100644
--- a/.github/workflows/build-release-task.yml
+++ b/.github/workflows/build-release-task.yml
@@ -57,6 +57,8 @@ jobs:
       branch: ${{ inputs.branch }}
       # Push to NuGet.org, never on a smoke build.
       push: ${{ inputs.nuget && !inputs.smoke }}
+      # Skip the artifact zip/upload on smoke (nothing consumes it on a PR).
+      smoke: ${{ inputs.smoke }}
 
   github-release:
     name: Publish GitHub release job
diff --git a/.husky/task-runner.json b/.husky/task-runner.json
index 009e6b3..dbcd960 100644
--- a/.husky/task-runner.json
+++ b/.husky/task-runner.json
@@ -15,7 +15,7 @@
             ]
         },
         {
-            "name": ".Net Format",
+            "name": ".NET Format",
             "command": "dotnet",
             "args": [
                 "format",
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 5dbeaba..0047099 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -5,7 +5,7 @@
             "name": "LanguageTagsCreate",
             "type": "coreclr",
             "request": "launch",
-            "preLaunchTask": ".Net Build",
+            "preLaunchTask": ".NET Build",
             "program": "${workspaceFolder}/.artifacts/bin/LanguageTagsCreate/debug/LanguageTagsCreate.dll",
             "args": [
                 "--codepath",
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index f8ae84a..6c819ef 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -1,8 +1,11 @@
 {
     "version": "2.0.0",
     "tasks": [
+        // .NET language group. A non-.NET repo drops this group and adds its own
+        // language's tasks. The first three tasks are the .NET clean-compile set
+        // (CODESTYLE.md) carried verbatim; the rest are convenience/project-specific.
         {
-            "label": ".Net Build",
+            "label": ".NET Build",
             "type": "process",
             "command": "dotnet",
             "args": [
@@ -20,7 +23,7 @@
             }
         },
         {
-            "label": ".Net Format",
+            "label": ".NET Format",
             "type": "process",
             "command": "dotnet",
             "args": [
@@ -37,9 +40,10 @@
                 "showReuseMessage": false,
                 "clear": false
             },
+            "dependsOrder": "sequence",
             "dependsOn": [
                 "CSharpier Format",
-                ".Net Build"
+                ".NET Build"
             ]
         },
         {
@@ -60,8 +64,9 @@
                 "clear": false
             }
         },
+        // Convenience / project-specific tasks (adapt or drop per repo).
         {
-            "label": ".Net Tool Update",
+            "label": ".NET Tool Update",
             "type": "process",
             "command": "dotnet",
             "args": [
@@ -94,7 +99,7 @@
             }
         },
         {
-            "label": ".Net Outdated Upgrade",
+            "label": ".NET Outdated Upgrade",
             "type": "process",
             "command": "dotnet",
             "args": [
diff --git a/AGENTS.md b/AGENTS.md
index 8efe587..27716ca 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -138,7 +138,8 @@ Anti-pattern: don't keep flipping the code on the same style point. Flip the rul
 This repo is derived from [`ptr727/ProjectTemplate`](https://github.com/ptr727/ProjectTemplate) and re-syncs against it periodically, not just at creation.
 
 - **Verbatim carries.** Pull the current template version of each shared artifact and re-apply it, adapting only this repo's placeholders: [`.github/copilot-instructions.md`](./.github/copilot-instructions.md) (the Copilot review runbook - change only the `<owner>`/`<repo>`/`<N>` values in its API snippets), [`.markdownlint-cli2.jsonc`](./.markdownlint-cli2.jsonc), [`.editorconfig`](./.editorconfig), [`.gitattributes`](./.gitattributes), and this file's [PR Review Etiquette](#pr-review-etiquette) section. The `.editorconfig` EOL/per-extension block is always-verbatim; its `[*.cs]`/ReSharper block is .NET-only and is carried here. Keep `copilot-instructions.md` **narrow** (provider mechanics plus the commit/PR-title summary); project-specific conventions and API contracts live in this file (see [Library API Conventions](#library-api-conventions)), not there - non-Copilot agents are not directed to that file.
-- **CODESTYLE.md.** Keep [`CODESTYLE.md`](./CODESTYLE.md) as the full aggregate the template ships and re-sync the whole file, rather than hand-trimming per-language snippets.
+- **CODESTYLE.md.** Re-sync the whole file from the template, then keep the **General** section plus the **.NET** language section and drop the language sections this repo doesn't ship (the per-language sections are droppable, exactly like the `.editorconfig` `[*.cs]` block; this repo is .NET-only, so the Python section is dropped). Repo-root placement is load-bearing - `AGENTS.md` and `.github/copilot-instructions.md` link it by relative path. Adapt the in-section repo-specific bits: the .NET project-folder list, the `InternalsVisibleTo` project names, and the VS Code task labels. Replacing the file wholesale and dropping whole sections is simpler to keep current than hand-editing per-language snippets.
+- **.vscode/tasks.json.** Carry the named **clean-compile** task definitions verbatim - `.NET Build`, `CSharpier Format`, and `.NET Format` (which chains the first two then `dotnet format style --verify-no-changes`). Their names are owned by the `CODESTYLE.md` ".NET" section and their command sequence + arguments are the canonical clean-compile spec; don't loosen them. Convenience tasks (`.NET Tool Update`, `.NET Outdated Upgrade`, `Husky.Net Run`) are the adapt zone.
 - **Release notes.** Keep a short release-notes summary in [`README.md`](./README.md) and the full history in [`HISTORY.md`](./HISTORY.md); update both when cutting a release.
 - **Report drift upstream.** When a re-sync surfaces a template gap, an outdated instruction, or something that bit this repo and would bite the next derived repo, open an issue in [`ptr727/ProjectTemplate`](https://github.com/ptr727/ProjectTemplate) rather than only patching locally - the template is the single source of truth, and this upstream-issue rule is this repo's only cross-repo obligation. Do not maintain or reference a "known downstream" registry, and do not name sibling repositories in docs, comments, or workflows - that registry and the maintainer fan-out duty live in the template hub only.
 
diff --git a/CODESTYLE.md b/CODESTYLE.md
index 462f801..12241e8 100644
--- a/CODESTYLE.md
+++ b/CODESTYLE.md
@@ -1,74 +1,100 @@
 # Code Style and Formatting Rules
 
-## Build Requirements
+This is the single code-style guide for the repo. The **General** section applies to every language and is always carried. Each **language section** is self-contained and **droppable**: a repo with no .NET side drops the .NET section - the same per-language model as [`.editorconfig`](./.editorconfig), whose `[*.cs]` block a non-.NET repo drops.
 
-### Zero Warnings Policy
+Cross-cutting *process* rules (PR titles, branching, US English, markdown style, comments philosophy, workflow YAML, PR review etiquette) live in [AGENTS.md](./AGENTS.md) and are not repeated here.
+
+## General
+
+These rules apply to every language in the repo.
+
+### Tooling Names and Casing
+
+Use each tool's official casing in task labels, docs, and prose - `.NET` (not `.Net`), `CSharpier`, `Husky.Net`. Don't invent personal variants.
+
+### Clean-Compile Verification
+
+Each language defines a **clean-compile** verification - the combination of build, formatter, linter, and code-analysis tools that must report clean before a commit. It is exposed as one or more **named** VS Code tasks (or, where a language ships no tasks, documented commands), and those definitions are **carried verbatim** across derived repos. The concrete names live in each language section below.
+
+- **Run it after every code change.** The relevant language's clean-compile must pass before you commit; CI runs the same checks as a backstop.
+- **The named task definition is the canonical spec** - its exact command sequence, arguments, and strictness. You may run it through the VS Code task **or** by invoking the equivalent native commands directly; either is fine **only if the sequence, arguments, and strictness match exactly**. No shortcuts and no more-lenient options (for example, never drop `--verify-no-changes` or loosen a `--severity`).
+
+### Analyzer Diagnostics and Suppressions
+
+- **A new port is not a license to silence diagnostics.** Brownfield / just-ported status never justifies relaxing analyzer or linter severities or muting newly surfaced warnings - fix them. (The only brownfield allowance in this template is the one-time git-signing / line-ending migration described in [AGENTS.md](./AGENTS.md) and [README.md](./README.md), which has nothing to do with code analysis.)
+- **Suppress only genuine false-positives or deliberate, documented exceptions**, always at the **narrowest scope that fits**, in this order of preference:
+  1. An **in-code annotation on the specific symbol**, with a justification - the language's attribute/comment form, never a blanket pragma spanning a region.
+  2. The **owning project's local config** when the exception is project-wide for one project (e.g. a test project's own `.editorconfig`).
+  3. The **root / shared config** only when the suppression is genuinely applicable to **every** project in the repo.
+- **Never blanket-relax a batch of rules project-wide** to get a port to build. The per-language mechanics (which attribute, which config key) are in each language section.
+
+### Markdown and Spelling
+
+These apply repo-wide, in every directory:
+
+1. **Markdown linting**: All `.md` files must be lint-clean (error and warning free) via the VS Code `markdownlint` extension. [`.markdownlint-cli2.jsonc`](./.markdownlint-cli2.jsonc) at the repo root is the single source of truth - the davidanson `markdownlint` extension and a command-line `markdownlint-cli2` run both read it, so the IDE and CLI stay in lock-step. Rules it deliberately disables (e.g. `MD013` line-length, `MD033` inline HTML) are **intentional** - do not "fix" them. This file is carried verbatim by every derived repo (see [AGENTS.md "Staying in Sync with the Template"](./AGENTS.md#staying-in-sync-with-the-template)). Fix violations at the source rather than disabling rules.
+2. **Spelling**: All spelling must be clean via the CSpell VS Code integration; words must be correctly spelled in **US English** (the repo-wide convention - see [AGENTS.md](./AGENTS.md)). Project-specific terms go in the workspace CSpell config.
+
+## .NET
+
+*This section applies only to the .NET side. A repo with no .NET projects drops the whole section.*
+
+This is the style guide for the **.NET projects** in this repo: [`LanguageTags/`](./LanguageTags/) (the published `ptr727.LanguageTags` library), [`LanguageTagsCreate/`](./LanguageTagsCreate/) (the codegen CLI), and [`LanguageTagsTests/`](./LanguageTagsTests/) (the xUnit suite).
+
+### Build Requirements
+
+#### Zero Warnings Policy
 
 **CRITICAL**: All builds must complete without warnings. The project enforces this through:
 
-1. **VS Code tasks**
-   - `CSharpier Format` → `.Net Build` → `.Net Format`
-   - `.Net Format` must pass with `--verify-no-changes` before commit
-   - Command: `dotnet format style --verify-no-changes --severity=info --verbosity=detailed`
+1. **The `.NET Format` clean-compile task** (see [Clean-Compile Verification](#clean-compile-verification))
+   - The .NET clean-compile is the **`.NET Format`** VS Code task, which chains `CSharpier Format` -> `.NET Build` -> `dotnet format style --verify-no-changes`. These three task definitions are carried verbatim in [`.vscode/tasks.json`](./.vscode/tasks.json).
+   - After any code change it must pass before commit. Run the `.NET Format` task. To run it natively instead, reproduce that task chain from [`.vscode/tasks.json`](./.vscode/tasks.json) exactly - `CSharpier Format`, then `.NET Build`, then the `dotnet format style --verify-no-changes --severity=info ...` verify - without dropping or loosening any argument (tasks.json is the canonical command spec). Bare `dotnet format` alone, skipping CSharpier or the build, is not sufficient.
 
 2. **Analyzer configuration**
    - `<AnalysisLevel>latest-all</AnalysisLevel>`
    - `<EnableNETAnalyzers>true</EnableNETAnalyzers>`
-   - Analyzer severity is `suggestion`, but all warnings must be addressed
+   - Analyzer severity is `suggestion`, but all warnings must be addressed - see [Analyzer Diagnostics and Suppressions](#analyzer-diagnostics-and-suppressions); do not relax rules to dodge them.
 
-3. **Husky.Net pre-commit hooks**
-   - Automated checks run before commits
+3. **CI lint backstop**
+   - `dotnet csharpier check` and `dotnet format style --verify-no-changes` run on every PR
+   - Husky.Net pre-commit hooks run the same checks locally (see Code Formatting and Tooling below)
 
-### Build Tasks
+#### Build Tasks
 
-Available VS Code tasks (use via `run_task` tool):
+Available VS Code tasks (run them from VS Code's task runner - **Terminal -> Run Task** - or an agent's task-running tool). The first three are the clean-compile set, carried verbatim; the rest are convenience tasks a derived repo adapts or drops:
 
-- `.Net Build`: Build with diagnostic verbosity
-- `.Net Format`: Verify formatting and style (must pass)
-- `CSharpier Format`: Auto-format code with CSharpier
-- `.Net Tool Update`: Update dotnet tools
-- `Husky.Net Run`: Run pre-commit hooks manually
+- `.NET Build`: Build with diagnostic verbosity *(clean-compile)*
+- `CSharpier Format`: Auto-format code with CSharpier *(clean-compile)*
+- `.NET Format`: Run CSharpier and build, then verify formatting and style with `--verify-no-changes` *(clean-compile; the task to run after edits)*
+- `.NET Tool Update`: Update dotnet tools *(convenience)*
+- `.NET Outdated Upgrade`: Upgrade outdated NuGet dependencies, interactive prompt *(convenience)*
+- `Husky.Net Run`: Run the configured Husky.Net pre-commit hooks manually *(convenience)*
 
-## Tooling and Editor
+### Tooling and Editor
 
-### Code Formatting and Tooling
+#### Code Formatting and Tooling
 
 1. **CSharpier**: Primary code formatter
-   - Run before committing: `dotnet csharpier format --log-level=debug .`
-
+   - Invoked by the `CSharpier Format` task / `dotnet csharpier format --log-level=debug .`
 2. **dotnet format**: Style verification
    - Verify no changes: `dotnet format style --verify-no-changes --severity=info --verbosity=detailed`
-
-3. **Husky.Net**: Git hooks for automated checks
-   - Installed as a local dotnet tool (via `dotnet tool restore`)
-   - Install Git hooks locally with `dotnet husky install`
-   - Pre-commit hooks run formatting and style checks
-
-4. **Other tools**
+3. **Other tools**
    - `dotnet-outdated-tool`: Dependency update checks
    - Nerdbank.GitVersioning: Version management
 
-### Editor Baseline
+Husky.Net runs the clean-compile checks as pre-commit git hooks. It is installed as a local dotnet tool (`dotnet tool restore`); run `dotnet husky install` to register the hooks. CI runs the same checks as a backstop.
+
+#### Editor Baseline
 
 1. **Required VS Code extensions**: CSharpier, markdownlint, CSpell
 2. **VS Code settings**: Use the workspace settings without overrides
 
-### Markdown Files
-
-1. **Linting**: All `.md` files must be linted with the VS Code `markdownlint` extension (local only; no CI)
-2. **Zero warnings**: Markdown linting must be error and warning free
-
-### Spelling
-
-1. **CSpell**: All spelling checks must be error free using the CSpell VS Code integration
-2. **Accepted spellings**: Words must be correctly spelled in US or UK English
-3. **Allowed exceptions**: Project-specific terms must be added to the workspace CSpell config
-
-## Coding Standards and Conventions
+### Coding Standards and Conventions
 
 Note: Code snippets are illustrative examples only. Replace namespaces/types to match your project.
 
-### C# Language Features
+#### C# Language Features
 
 1. **File-scoped namespaces**
 
@@ -104,7 +130,7 @@ Note: Code snippets are illustrative examples only. Replace namespaces/types to
    var name = "test";
    ```
 
-### Naming Conventions
+#### Naming Conventions
 
 1. **Private fields**: underscore prefix with camelCase
 
@@ -125,7 +151,7 @@ Note: Code snippets are illustrative examples only. Replace namespaces/types to
    private const int MaxRetries = 3;
    ```
 
-### Code Structure
+#### Code Structure
 
 1. **Global usings**: Use `GlobalUsings.cs` for common namespaces
 
@@ -169,9 +195,9 @@ Note: Code snippets are illustrative examples only. Replace namespaces/types to
    - Linux scripts (`.sh`): LF
 
 6. **`#region`**: Do not use regions. Prefer logical file/folder/namespace organization.
-7. **Member ordering (StyleCop SA1201)**: const → static readonly → static fields → instance readonly fields → instance fields → constructors → public (events → properties → indexers → methods → operators) → non-public in same order → nested types
+7. **Member ordering (StyleCop SA1201)**: const -> static readonly -> static fields -> instance readonly fields -> instance fields -> constructors -> public (events -> properties -> indexers -> methods -> operators) -> non-public in same order -> nested types
 
-### Comments and Documentation
+#### Comments and Documentation
 
 1. **XML documentation**
    - `<GenerateDocumentationFile>true</GenerateDocumentationFile>`
@@ -202,20 +228,25 @@ Note: Code snippets are illustrative examples only. Replace namespaces/types to
    public async Task<string> GetQuoteOfTheDayAsync(string category, CancellationToken cancellationToken) {}
    ```
 
-2. **Code analysis suppressions**
-   - Do not use `#pragma` sections to disable analyzers
-   - For one-off cases, use suppression attributes with justifications
-   - For project-wide suppressions, add rules to `.editorconfig`
+#### Analyzer Suppressions (.NET)
 
-   ```csharp
-   [System.Diagnostics.CodeAnalysis.SuppressMessage(
-       "Design",
-       "CA1034:Nested types should not be visible",
-       Justification = "https://github.com/dotnet/sdk/issues/51681"
-   )]
-   ```
+Follow the scope hierarchy in [Analyzer Diagnostics and Suppressions](#analyzer-diagnostics-and-suppressions). .NET mechanics, narrowest first:
+
+- **Never use `#pragma warning disable`** to silence an analyzer.
+- **Symbol-scoped**: a `[System.Diagnostics.CodeAnalysis.SuppressMessage(...)]` attribute with a `Justification`, on the specific member or type:
+
+  ```csharp
+  [System.Diagnostics.CodeAnalysis.SuppressMessage(
+      "Design",
+      "CA1034:Nested types should not be visible",
+      Justification = "https://github.com/dotnet/sdk/issues/51681"
+  )]
+  ```
+
+- **Project-scoped** (e.g. a test project): a `dotnet_diagnostic.<RULE>.severity` entry in *that project's own* `.editorconfig`, with a comment explaining why.
+- **Repo-wide**: a `dotnet_diagnostic.<RULE>.severity` entry in the root `.editorconfig`, only when the rule is genuinely not applicable to any project. Relaxing a batch of `CA*` rules (or `dotnet_analyzer_diagnostic.severity`) to push a brownfield port through the build is exactly what this forbids.
 
-### Error Handling and Logging
+#### Error Handling and Logging
 
 1. **Serilog logging**: Use structured logging
 
@@ -247,7 +278,7 @@ Note: Code snippets are illustrative examples only. Replace namespaces/types to
 
 5. **Exceptions**: Do not swallow exceptions; log and rethrow or translate to a domain-specific exception
 
-### Code Patterns
+#### Code Patterns
 
 1. **Guard clauses**: Prefer early returns for validation and error handling
 2. **Async all the way**: Avoid blocking calls (`.Result`, `.Wait()`); use `async`/`await`
@@ -264,7 +295,7 @@ Note: Code snippets are illustrative examples only. Replace namespaces/types to
 12. **Read-only data**: Use immutable or frozen collections for read-only data sets
 13. **Lazy initialization**: Use `Lazy<T>` for static, thread-safe instantiation (e.g., logger factory, HTTP factory)
 
-### Testing Conventions
+#### Testing Conventions
 
 1. **Framework**: xUnit with AwesomeAssertions
 
@@ -287,7 +318,7 @@ Note: Code snippets are illustrative examples only. Replace namespaces/types to
 3. **Naming**: Descriptive names with underscores
 4. **Theory tests**: Use `[Theory]` with `[InlineData]`
 
-## Project Configuration
+### Project Configuration
 
 1. **Target framework**: .NET 10.0 (`<TargetFramework>net10.0</TargetFramework>`)
 
@@ -300,15 +331,14 @@ Note: Code snippets are illustrative examples only. Replace namespaces/types to
    - Include SourceLink: `<PublishRepositoryUrl>true</PublishRepositoryUrl>`
    - Embed untracked sources: `<EmbedUntrackedSources>true</EmbedUntrackedSources>`
 
-4. **Internal visibility**: Use `InternalsVisibleTo` for test and benchmark access
+4. **Internal visibility**: Use `InternalsVisibleTo` for test access (adapt the project names to your repo's test projects)
 
    ```xml
    <ItemGroup>
-     <InternalsVisibleTo Include="Benchmarks" />
-     <InternalsVisibleTo Include="Tests" />
+     <InternalsVisibleTo Include="LanguageTagsTests" />
    </ItemGroup>
    ```
 
-## Best Practices
+### Best Practices
 
 1. **Code reviews**: All changes go through pull requests

From 526e29a69a97e7305d7f16b3eb00f1f6a5cea7e7 Mon Sep 17 00:00:00 2001
From: Pieter Viljoen <ptr727@users.noreply.github.com>
Date: Mon, 22 Jun 2026 10:00:59 -0700
Subject: [PATCH 2/2] Fix CODESTYLE CI lint backstop to match husky-run PR
 workflow

The carried template line claimed CI runs `dotnet csharpier check`;
this repo's test-pull-request.yml runs `dotnet husky run` (CSharpier +
`dotnet format style --verify-no-changes`). Describe the actual CI.
---
 CODESTYLE.md | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/CODESTYLE.md b/CODESTYLE.md
index 12241e8..1dd97b8 100644
--- a/CODESTYLE.md
+++ b/CODESTYLE.md
@@ -57,8 +57,7 @@ This is the style guide for the **.NET projects** in this repo: [`LanguageTags/`
    - Analyzer severity is `suggestion`, but all warnings must be addressed - see [Analyzer Diagnostics and Suppressions](#analyzer-diagnostics-and-suppressions); do not relax rules to dodge them.
 
 3. **CI lint backstop**
-   - `dotnet csharpier check` and `dotnet format style --verify-no-changes` run on every PR
-   - Husky.Net pre-commit hooks run the same checks locally (see Code Formatting and Tooling below)
+   - [`test-pull-request.yml`](./.github/workflows/test-pull-request.yml) runs `dotnet husky run` on every PR - the same CSharpier + `dotnet format style --verify-no-changes` checks the pre-commit hooks run locally
 
 #### Build Tasks
 
