Repository navigation
fix(toolchain): make the documented C# gates execute truthfully - #540
Merged
drmoisan merged 2 commits intoAug 11, 2026
Conversation
The documented C# toolchain commands could not do what they claim. Three independent defects made the format, analyzer and type-check gates either unrunnable or unable to fail: - #509: `dotnet tool run csharpier .` is CSharpier v0 syntax. dotnet-tools.json pins 1.2.6, which requires a `format`/`check` subcommand, so the documented form exits 1 with "Required command was not provided." - #512/#492: `/t:Build` lets MSBuild's incremental up-to-date check skip CoreCompile on all 18 projects, because the check does not invalidate on a command-line `/p:` change. A warm run returned exit 0 having compiled nothing. Measured: 18 of 18 projects skipped. - #522: `/p:Nullable=enable` is deliberately absent from ci.yml because nullable analysis here is per-file opt-in via `#nullable enable`. Forcing it solution-wide conscripts every un-annotated file and produces 195 errors in UtilitiesCS.csproj that are red on a clean main, so the documented gate could never pass. Corrections, at the enumerated sites only: - Format: `dotnet tool run csharpier format .` (verify with `check .`), always through `dotnet tool run` so the pinned version is used. - Analyze and type-check: `/t:Rebuild /m`, with the type-check command now character-for-character ci.yml's. - Rationale prose carried in-line at every corrected site so a future agent does not "restore" the removed flag. - csharp-qa-gate SKILL.md now requires a `/fl` log with zero occurrences of `Skipping target "CoreCompile"`; exit 0 with a non-zero skip count is unverified, not passed. Executable carrier: Invoke-VSBuild.ps1 gains a `-Target` parameter (ValidateSet Build/Rebuild, default Build so the `build:` task is unchanged); `-EnableNullable` is retained but now emits a deprecation warning instead of the property. The lint and type-check VS Code tasks pass `-Target Rebuild`. No policy requirement is relaxed. The change is a strengthening: gates that returned exit 0 without compiling now compile, and a gate that could never pass now can. The 195 nullable diagnostics the corrected gate exposes are measured and attributed for a follow-on burn-down epic, not fixed here. Files: CLAUDE.md (C# toolchain command block only; § UT2 byte-unchanged), .claude/rules/csharp.md, .claude/skills/csharp-qa-gate/SKILL.md, scripts/vscode/Invoke-VSBuild.ps1, .vscode/tasks.json, and the Pester tests. Closes #512 Closes #492 Closes #509 Closes #522 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Feature review of bug/csharp-toolchain-gate-fidelity-512 against the epic integration branch. Zero blocking findings; all 13 acceptance criteria verified against evidence rather than accepted from the executor's check-off. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
drmoisan
added a commit
that referenced
this pull request
Aug 11, 2026
…g the final gate 512 merged via PR #540 (22eaee8), worktree removed. Wave 0 complete. Records that the integrated-tree workflow_dispatch run failed on a single intermittent test that also fails on main at this branch's base commit, with the same 6435 total, and characterizes its wall-clock race mechanism. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
drmoisan
added a commit
that referenced
this pull request
Aug 15, 2026
Three updates following the delivery of issue #512 (PR #540): - Mark the "CLAUDE.md nullable command diverges from ci.yml" memory RESOLVED. The documented C# commands now match ci.yml, so the old advice ("reproduce ci.yml's command before accepting a nullable blocker") no longer describes a live divergence. A future appearance of `/p:Nullable=enable` or `/t:Build` in a documented command is now a regression of #512/#522, and the memory says so. Also records the measured 195-error UtilitiesCS figure, with its lower-bound qualification, for the #492 burn-down. - Add a memory for the untracked `coverage.xml` that PoshQC test runs drop at the repository root. It is in neither .gitignore nor .csharpierignore, so it inflates the CSharpier file count between two otherwise-identical runs and can be swept into a diff by `git add -A`. - Correct the analyzer-vacuity memory: the non-vacuity assertion must be a zero `Skipping target "CoreCompile"` count, not a csc.exe count. csc.exe occurrences are zero at verbosity=normal even for genuine compiles, so the previously recorded csc-count acceptance would have been unsatisfiable. The index was compacted concurrently by a sibling; this commit takes the sibling's compaction as the base and applies only the three deltas above. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
drmoisan
added a commit
that referenced
this pull request
Aug 15, 2026
…g the final gate 512 merged via PR #540 (22eaee8), worktree removed. Wave 0 complete. Records that the integrated-tree workflow_dispatch run failed on a single intermittent test that also fails on main at this branch's base commit, with the same 6435 total, and characterizes its wall-clock race mechanism. Co-Authored-By: Claude Opus 5 (1M context) <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.
fix(toolchain): make the documented C# gates execute truthfully
Summary
CLAUDE.md,.claude/rules/csharp.md, and.claude/skills/csharp-qa-gate/SKILL.mdhad drifted from.github/workflows/ci.ymland from the pinned tool versions.dotnet tool run csharpier .is CSharpier v0 syntax;dotnet-tools.jsonpins 1.2.6, which requires a subcommand. Measured:EXIT_CODE: 1,Required command was not provided.Corrected tocsharpier format .withcsharpier check .for read-only CI parity./t:Buildlets MSBuild's incremental up-to-date check skipCoreCompile, because that check compares timestamps and does not invalidate on a command-line/p:change. Measured on a warm tree:EXIT_CODE: 0withSkipping target "CoreCompile"on 18 of 18 projects. Corrected to/t:Rebuild /m./p:Nullable=enableis deliberately absent fromci.ymlbecause nullable analysis here is per-file opt-in via#nullable enable. Forcing it solution-wide conscripts every file that never adopted the pragma. Measured: 195 errors inUtilitiesCS.csprojagainst 0 without it. Removed, making the documented command character-for-characterci.yml's.scripts/vscode/Invoke-VSBuild.ps1gains a-Targetparameter and the VS Codelint:andtype-check:tasks pass-Target Rebuild./fllog with zero occurrences ofSkipping target "CoreCompile". Exit 0 with a non-zero skip count is now explicitly unverified, not passed.Why
Two deliveries on 2026-08-08 (#507, #508) required a human override because the documented nullable gate manufactured blocking findings that were red on a clean
main. Separately, every agent that ran the documented analyzer or type-check command on a warm working tree received a green result from a build that compiled nothing. The governance documents were the defect: they described gates that were structurally incapable of doing their job.This change corrects those documents. It is a strengthening, not a relaxation, and that distinction is load-bearing:
/p:Nullable=enableloses no enforcement over any file that has opted in. No project in this repository carries a<Nullable>element and there is noDirectory.Build.props, so the flag was a solution-wide opt-in, not a per-file one./p:TreatWarningsAsErrors=truestill promotes everyCS86xxdiagnostic in every#nullable enablefile to a build error. A negative-path control confirms this: a deliberately introduced nullable violation in a pragma-carrying production file still fails the corrected gate./t:Buildwith/t:Rebuildstrictly increases what the gate examines, from nothing (warm) to all 18 projects.The corrected sites carry in-line rationale so that a future agent does not "restore" the removed flag, which has already happened once in the record.
What Changed
Governance documents (authorized sites only)
CLAUDE.md.claude/rules/csharp.md.claude/skills/csharp-qa-gate/SKILL.mdCLAUDE.md§ UT2,.claude/rules/general-unit-test.md, and.claude/rules/quality-tiers.mdare byte-unchanged and were verified by a zero-line diff and matching SHA-256 against the merge base.AGENTS.md,.agents/**,.github/instructions/**,.github/agents/**,.codex/**, and.github/workflows/ci.ymlare absent from the diff.Executable carrier
scripts/vscode/Invoke-VSBuild.ps1: new-Targetparameter,[ValidateSet('Build','Rebuild')], defaulting toBuildso the existingbuild:task is unchanged.Get-MSBuildBuildArgumentsemits"/t:$Target"in the same array position. The-EnableNullableswitch is retained for compatibility but now emits a deprecationWrite-Warninginstead of theNullable=enableproperty..vscode/tasks.json: thelint:andtype-check:tasks pass-Target Rebuild; thetype-check:task drops-EnableNullableand retains-TreatWarningsAsErrors.Tests
tests/scripts/vscode/Invoke-VSBuild.Tests.ps1: one newItasserting-Target Rebuildemits/t:Rebuild, and the nullable-mappingItrewritten to assert-EnableNullableemits no MSBuild property. Both were run red before the fix and green after.Documentation and evidence
docs/features/active/2026-08-10-csharp-toolchain-gate-fidelity-512/evidence/{baseline,qa-gates,regression-testing,issue-updates}/, each recordingTimestamp:,Command:,EXIT_CODE:andOutput Summary:.policy-audit,code-review,feature-audit.Architecture / How It Fits Together
There are two carriers of the same command, and both had to move together.
The normative carrier is prose:
CLAUDE.mdand.claude/rules/csharp.mdstate the commands that every agent is required to run, and.claude/skills/csharp-qa-gate/SKILL.mdis the gate procedure agents invoke. The executable carrier isscripts/vscode/Invoke-VSBuild.ps1, reached through.vscode/tasks.json, which is what a developer actually runs from the editor. Correcting only the prose would have left the tasks emitting/t:Build; correcting only the script would have left agents reading a defective instruction. Both are corrected, and the Pester suite pins the script's argument construction so the executable side cannot silently drift back..github/workflows/ci.ymlis the third carrier and was already correct. It is the reference the other two are now reconciled against, and it is untouched by this PR.One deliberate asymmetry remains and is documented in-line: CI's analyzer step uses
/t:Build /mwhile the documented local command uses/t:Rebuild /m. A runner checkout is always cold, so/t:Buildis non-vacuous there; a local working tree is not.Verification
Completed
dotnet tool run csharpier format .EXIT_CODE: 0— Formatted 1517 filesdotnet tool run csharpier check .EXIT_CODE: 0— Checked 1517 filesmsbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:EnableNETAnalyzers=true /p:EnforceCodeStyleInBuild=trueEXIT_CODE: 0— 0 Error(s), 0CoreCompileskipsmsbuild TaskMaster.sln /t:Rebuild /m /p:Configuration=Debug "/p:Platform=Any CPU" /p:TreatWarningsAsErrors=trueEXIT_CODE: 0— 0 Error(s), 0CoreCompileskipsscripts/vscode,tests/scripts/vscode)EXIT_CODE: 0— zero files rewrittenEXIT_CODE: 1— 16 findings, multiset identical to the merge-base baseline, zero newtests/scripts/vscode)EXIT_CODE: 0scripts/vscode/Invoke-VSBuild.ps1Controls, both recorded as evidence:
EXIT_CODE: 0against an unperturbed clean checkout. The gate is passable.#nullable enableproduction file (UtilitiesCS/Extensions/QueueExtensions.cs) causes the corrected gate to return non-zero with the expectedCS86xxdiagnostic. The perturbation was reverted; no*.csfile appears in this diff. The proof is non-vacuous — the log confirms the perturbed file's project was genuinely recompiled with zeroCoreCompileskips.All 13 acceptance criteria pass. Feature review reports 0 blocking findings, with each executor check-off independently re-verified against evidence rather than accepted.
Recommended
Deliberate scope deviations, recorded rather than silent
*.cs,*.csproj,*.propsor*.targetsfile, andRun MSTest suite with coverageis failing onmainfor reasons unrelated to this feature. Recorded at plan task[P6-T9].Invoke-VSBuild.ps1itself. The acceptance condition is a finding multiset identical to the baseline, which holds;EXIT_CODE: 0was never asserted for that step.csc.exeinvocation count as the non-vacuity proof, but that count is zero atverbosity=normaleven for genuine compiles. The zero-Skipping target "CoreCompile"assertion is used instead and is strictly more discriminating. Recorded as a formal deviation inspec.md; no criterion text was changed.Backward Compatibility / Migration Notes
Invoke-VSBuild.ps1's interface.-Targetdefaults toBuild, so the existingbuild:task and every unqualified caller behave exactly as before.-EnableNullablestill binds; it now warns and emits nothing rather than failing.dotnet tool restoreonce per clone or worktree before the first CSharpier invocation. This is now stated at the documentation site.Nullable=enableproperty is no longer emitted by any documented command or VS Code task. Anything that depended on the forced solution-wide behavior will see the 195 latent diagnostics stop appearing — which was the intended state, since they were never enforceable.Risks and Mitigations
/p:Nullable=enableor/t:Build, reading the removal as a relaxation. This has already happened once.spec.mdand above./t:Rebuildis slower than a warm/t:Build./t:Buildwas fast because it compiled nothing./mis added to parallelize. Cold and warm elapsed times are recorded in the baseline evidence./p:Nullable=enable, which the corrected command does not pass. The corrected gate is green on this branch. The 195 are measured and attributed for a follow-on epic.Review Guide
Suggested order:
CLAUDE.md,.claude/rules/csharp.md,.claude/skills/csharp-qa-gate/SKILL.md— the substantive change. Confirm each edit sits in the C# toolchain command block and that § UT2 is untouched.scripts/vscode/Invoke-VSBuild.ps1and.vscode/tasks.json— the executable carrier, 16 and 4 changed lines respectively.tests/scripts/vscode/Invoke-VSBuild.Tests.ps1— the pinning tests.evidence/qa-gates/typecheck-positive-control.*andtypecheck-negative-control.*— the two controls that establish the corrected gate both passes and fails correctly. These are the load-bearing artifacts.evidence/qa-gates/no-relaxation-review.*andprotected-files-zero-diff.*— the AC8/AC9 guards.The bulk of the diff by line count is evidence artifacts, which are append-only records and can be skimmed.
Follow-ups
/p:Nullable=enableprobe reports 195 errors, all attributed toUtilitiesCS.csproj: CS8766 x130, CS8618 x23, CS8625 x12, CS8600 x9, CS8601 x8, CS8604 x7, CS8602 x3, CS8603 x2, CS8714 x1. This figure is a lower bound — the build aborted after 22 of 73CoreCompileexecutions, soUtilitiesCS's dependents never compiled. Sizing that epic should begin by measuring the solution-wide total. Explicitly out of scope here (Bug: nullable-gate-masked-by-incremental-build #492).#535tracks the.codex/**,.agents/**and.github/instructions/**copies that still document the defective commands./t:Rebuildversus CI's/t:Buildrationale is carried at the normative sites but omitted at three condensed documentation sites; a reviewer notedspec.mdSD2's prose and its replacement table are internally inconsistent on this point. Recommended to fold into Codex/Copilot instruction mirrors still document the CSharpier v0 command and the unpassable nullable command #535.GitHub Auto-close