Skip to content

Add a diff-scoped comment-hygiene ratchet for build.ps1/test.ps1 and CI - #1061

Open
johnml1135 wants to merge 5 commits into
mainfrom
comment-enforcement
Open

Add a diff-scoped comment-hygiene ratchet for build.ps1/test.ps1 and CI#1061
johnml1135 wants to merge 5 commits into
mainfrom
comment-enforcement

Conversation

@johnml1135

@johnml1135 johnml1135 commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Adds a mechanical, diff-scoped comment-hygiene gate enforcing the fieldworks-code-commenting standard, wired into build.ps1, test.ps1, and CI. Originally C#/PowerShell only; now also covers C/C++/IDL and the <!-- --> comments in project files and Avalonia views, since those carry real design rationale that was shipping unscanned. Comment line width is capped at .editorconfig's max_line_length rather than left unconstrained, and the 200-character content budget rises to 600 where the code a comment introduces is densely branching. Also fixes a repo-wide bash/PowerShell inconsistency traced back to a misconfigured skill permission that was letting agents default to Bash instead of PowerShell.

265 of the 266 changed .cs files are comment-only -- re-verified after the rebase by stripping comment and blank lines from each file's origin/main and HEAD versions and diffing the remainder, zero non-comment differences. The one exception is a new characterization test. The real review surface is the tooling/CI files below.

Where to look:

  • CommentHygiene.psm1's XML comment parser -- XML forbids a literal -- anywhere inside a <!-- --> comment, so the standard's usual em-dash-to--- fix is invalid there. A new mechanical check catches it; a single - is used instead.
  • The same module's new comment-line-too-long check -- it reads max_line_length and tab_width from .editorconfig itself instead of carrying its own number, so the comment limit can never drift from the one the rest of the repo follows. It measures display columns, not characters: this repo indents with tabs, so a comment four levels deep differs by twelve columns between the two. It applies to doc comments too, which are exempt from the content budget because of what they say, not because they may run off the screen. A local run re-wraps the line; CI reports it.
  • Measure-CodeComplexity and the extended budget -- a comment introducing a region scoring 10+ decision points gets 600 characters instead of 200, because a reader there needs the invariants spelled out and 200 buys about two sentences. Nothing opts in by hand. The threshold was calibrated against the corpus, not guessed (see Evidence).
  • The same module's file-read path -- Get-Content was ~50x slower than File.ReadAllLines on a large file, and git remote show origin added a network round-trip on every local run. The gate went from 5.4s to ~2.2s (confirmed by repeated timing).
  • .github/workflows/{CommitMessage,check-whitespace,stray-docs}.yml -- switched from shell: bash to shell: pwsh on their ubuntu-latest runners. check_results.log is pre-created before the gitlint pipe because Tee-Object, unlike bash's tee, never creates its target file when the piped command emits zero objects -- the common case on a clean lint run.
  • 87 comment-hygiene violations in the newly-covered file types trace (via git blame) to AI-authored commits and are fixed directly, verified comment-only. The remaining legacy violations are left for the ratchet to grandfather, same as the existing C#/PowerShell backlog.

Deliberately not here: C-style /* */ block comments still aren't scanned, in any language. Five now-redundant .sh scripts are deleted rather than kept as a bash fallback -- their .ps1 twins already existed and were already cross-platform-safe. The extended budget keys on measured branching only: a genuinely non-obvious problem/solution write-up sitting above straight-line code is not mechanically detectable and still gets 200 characters.

Verification: Rebased onto main; the three conflicting files resolved to main's versions and dropped out of the diff entirely (see Reversals). The diff-scoped gate is clean on its own diff, reached by letting the gate re-wrap the branch's own over-long comments across 210 files and hand-shortening six that the wrapper could not fit. Fixture suite green (expanded with coverage for the width rule, the wrapper, and the complexity budget). All 19 changed project-file/Avalonia-view files parse as XML. git diff --check clean. gitlint clean. Not stacked. CI has not yet run against the rebased HEAD.


Reading this a year from now -- start here

This branch originally shipped with a design spec and implementation plan under Docs/superpowers/. Both are deleted here: the plan was a task-by-task build log now fully superseded by the code, and the design doc's factual claims (PowerShell out of scope, a single non-ascii category flagging any non-ASCII byte, no comment-length redesign) no longer match what shipped. The durable reasoning from both is preserved below instead.

Decisions, and why
  • Ratchet, not zero-tolerance. Modeled on the sibling motif repo's tools/comment-hygiene.ps1, but adapted: new and touched comments must comply, the existing corpus is not blocked on a repo-wide sweep. A -Full report-only mode exists separately for finding what a file already carries before you touch it.
  • Judgment-based rules stay manual. Accuracy, WHAT-not-HOW framing, and standalone clarity aren't mechanically enforceable without high false-positive risk; only the banned-content categories, ASCII-punctuation, length, and width are checked.
  • A content budget and a width limit, doing different jobs. The 200-character budget bounds how much a comment says; .editorconfig's max_line_length bounds how wide any one line gets. They are independent, and the budget alone exerted no pressure on line width at all -- splitting a block across more lines costs nothing against it, so nothing stopped a 205-column line.
  • The width number is read, not chosen. Taking it from .editorconfig means there is exactly one line-length policy in the repo. Doc comments are subject to it despite being budget-exempt.
  • Complexity raises the budget automatically rather than by annotation. An explicit opt-in marker was the alternative; auto-detection was chosen so nothing has to be hand-annotated and the exception cannot be claimed for simple code. The cost is that the other case worth relief -- a non-obvious problem/solution description -- has no mechanism.
  • non-ascii-punctuation, not non-ascii. The category only flags a specific Western-typography punctuation set (em-dash, arrows, smart quotes), not all non-ASCII content -- this codebase legitimately quotes real script and IPA text in comments.
  • CI-vs-local auto-fix split. Auto-fixing and exiting 0 in CI would let a real violation merge behind a green check, since CI has no way to commit the fix back to the PR branch. Only local runs auto-fix; CI always reports and fails.
  • Xml's first <!-- --> block is exempt from the length budget. It plays the same file/type-summary role as a C# /// or a PowerShell help block, but Xml has no separate doc-comment syntax to mark it by. Without this, sixteen dialog- and project-level summary comments were losing content to a budget meant for one-line implementation notes.
  • Fix root causes with git blame, not guesses. The bash/PowerShell audit didn't start from "let's convert some scripts" -- it started from grepping every "bash" mention in the repo and blaming each one. Nearly all traced to a single commit that set the PowerShell skill's tool permission to route through Bash; that permission, not habit, was the actual mechanism.
Paths not taken
  • An explicit HYGIENE-EXTENDED: marker for over-200 comments: rejected in favour of measured complexity, so the exception cannot be attached to code that does not warrant it. Accepted cost stated above.
  • Raising the content budget globally instead of keying it to complexity: rejected because the problem is categorical, not a wrong number -- most over-200 comments are ordinary notes that should be shorter.
  • PSScriptAnalyzer alone for PowerShell compatibility: it only catches structural syntax additions via AST analysis under whichever engine runs it -- it cannot see that a backtick-escape sequence resolves differently under 5.1 vs 7, since both engines parse it successfully and just disagree on the resulting value. A dependency-free regex layer for known gotchas was added alongside it.
  • A <!--!-style marker for "this is a doc comment": rejected as inventing syntax the XML/MSBuild/XAML ecosystem doesn't have. The first-comment-in-document convention was already how every affected file was structured.
  • Keeping the .sh scripts as a bash fallback: rejected once their .ps1 twins were confirmed to already have #!/usr/bin/env pwsh shebangs and cross-platform-safe implementations -- verified by running check-whitespace.sh and check-whitespace.ps1 side by side and confirming identical output before deleting the original.
Reversals
  • The original design scoped this to .cs only, explicitly excluding PowerShell ("The skill's stated scope is C#"). PowerShell scanning was added after the tooling's own comments shipped unscanned.
  • An earlier iteration of the comment-length rule was a hard 1-physical-line cap rather than the 200-character budget that shipped; the budget is now paired with a separate per-line width limit, which is what the 1-line cap was reaching for.
  • The powershell skill advised preferring PowerShell Core syntax "where possible". That is backwards for this repo -- CI runs build.ps1 and test.ps1 under Windows PowerShell 5.1 -- and is now a dual-engine requirement pointing at powershell-compat.ps1.
  • Comment edits to WelcomeToFieldWorksDlg.cs, LexOptionsDlg.cs, and FwXWindow.cs are reverted: main had independently ASCII-fixed those same comments while keeping more of their text, and had rewritten the code under LexOptionsDlg.cs, so this branch's comment described logic that no longer exists. All three files now match main exactly.
  • Comment edits in eight further files are dropped. Their comments traced by git blame to unrelated commits spread from 2012 to 2026, rather than to the Avalonia conversion foundation and the four other commits this branch's churn actually belongs to.
Evidence
  • Comment-only diff claim (265/266 files): for every changed .cs file, stripped comment and blank lines from both the origin/main and HEAD versions and diffed the remainder -- zero non-comment differences across all but one (a genuinely new test file).
  • Churn provenance: git blame over every changed comment line resolved 1,621 of 1,742 (93%) to 31c0a6157, the Avalonia conversion foundation (LT-22625: Add the WinForms to Avalonia conversion foundation #964), then six days old. The eight files whose comments traced elsewhere are the ones dropped above.
  • Width was genuinely unenforced: measured over the full population of comment lines this branch adds, ~44% exceeded .editorconfig's 98 columns once tabs are counted as display columns, with a maximum of 205. The gate now reports 17,391 pre-existing comment-line-too-long violations repo-wide, all grandfathered by the ratchet.
  • Complexity threshold calibration: measured against every implementation-comment block already over 200 characters repo-wide. A threshold of 6 would extend 8.3% of them, 8 would extend 4.0%, and 10 extends 2.0% (81 blocks) -- 10 was chosen as both rare and the conventional McCabe "high complexity" line. Corroborated end to end: the full-repo comment-too-long count drops from 4,020 to 3,943 with the extension enabled.
  • Full-repo scan: 22,376 violations across 2,836 files, of which 17,391 are the newly-added width category and 3,943 the content budget; all pre-existing legacy debt, none blocking.
  • XML validity: parsed all 19 changed project-file/Avalonia-view files as XML ([xml] cast) after the wrap -- 0 invalid.
  • Whitespace: re-wrapping initially copied tab-plus-spaces continuation indents onto new lines, which git diff --check rejects under this repo's indent-with-non-tab setting. Xml comment indentation is normalised to tabs; git diff --check is clean.
  • PS 5.1 escape bug: a script printing the byte length of a map key built from `u{2014} returned 1 (the real em dash) under pwsh and 7 (the literal text u{2014}) under powershell.exe.
  • Gate performance: Get-Content measured at 51ms vs. File.ReadAllLines at 1ms on a 5446-line file; git remote show origin measured at ~515ms vs. git rev-parse --abbrev-ref origin/HEAD at ~31ms (no network call). End-to-end gate timing: 5.4s before, ~2.2s after, across repeated runs on both PowerShell engines.
  • AI-commit attribution: git blame plus a Co-Authored-By: Claude trailer check on all 1635 violations newly in scope from the C/C++/IDL/Xml extension found 87 across 27 files tracing to AI-authored commits (mostly #964); all 87 fixed and re-verified clean via -Full -List.
  • StrictMode defects found and fixed: powershell-compat.ps1 read $Global:FwPowerShellCompatAnalyzerUnavailable before assigning it, which errors on every run under Set-StrictMode -Version Latest. Seeded before use.

This change is Reviewable

Enforces the fieldworks-code-commenting standard's banned-content rules
and a non-ascii-punctuation check against lines a diff adds, wired into
build.ps1, test.ps1, and CI. Includes a triage tool that attributes
existing violations to their introducing commit, and a stray-docs CI
gate blocking brainstorming/planning markdown from surviving into a
merged PR.

The comment-length rule is a 200-character budget rather than a hard
1-line cap, since the cap forced every explanation onto one line that
then broke .editorconfig's line-length limit once genuinely non-trivial.
This retroactively surfaced 127 pre-existing over-long comments, all
tracing via git blame to the already-merged Avalonia foundation commit
(#964); all 127 are reformatted into multi-line prose here.

Adds a powershell-compat gate (regex scan plus best-effort
PSScriptAnalyzer) and dual-shell CI test runs, after an independent
review surfaced three functional bugs specific to Windows PowerShell
5.1 vs 7: a Set-StrictMode crash in local base-ref resolution, a CI
checkout missing fetch-depth that left origin/main unresolvable, and a
PowerShell 6+-only escape sequence that silently disabled ASCII
detection under 5.1 -- the exact engine CI's build.ps1/test.ps1 steps
run under. All three are fixed and verified by direct reproduction
under both engines, not just reasoned about.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@johnml1135
johnml1135 force-pushed the comment-enforcement branch from 5d614d3 to 7026fa0 Compare August 13, 2026 11:53
"ASCII-only" was misleading -- the rule only bans a specific punctuation
set (em-dash, arrows, smart quotes), not all non-ASCII content, which
legitimately appears in comments quoting real script or IPA text.

Also condenses fieldworks-code-commenting/SKILL.md from 203 to 129
lines: cuts the worked-examples section and tightens every other
section's prose. No rule content is removed, only reworded shorter.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@codecov-commenter

codecov-commenter commented Aug 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 38.07%. Comparing base (1674067) to head (093ba5f).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1061      +/-   ##
==========================================
+ Coverage   38.05%   38.07%   +0.02%     
==========================================
  Files        1499     1499              
  Lines      350117   350115       -2     
  Branches    40233    40233              
==========================================
+ Hits       133226   133299      +73     
+ Misses     187607   187541      -66     
+ Partials    29284    29275       -9     
Files with missing lines Coverage Δ
Lib/src/FormLanguageSwitch/FormLanguageSwitch.cs 0.00% <ø> (ø)
Src/CacheLight/MetaDataCache.cs 93.54% <ø> (ø)
Src/Common/Controls/DetailControls/DataTree.cs 43.76% <ø> (ø)
...rc/Common/Controls/DetailControls/ObjSeqHashMap.cs 84.21% <ø> (ø)
Src/Common/Controls/DetailControls/Slice.cs 39.47% <ø> (ø)
...rc/Common/Controls/XMLViews/XmlBrowseViewBaseVc.cs 68.16% <ø> (ø)
Src/Common/FieldWorks/WelcomeToFieldWorksDlg.cs 22.53% <ø> (ø)
Src/Common/Framework/MainWindowDelegate.cs 2.40% <ø> (ø)
Src/Common/FwAvalonia/AvaloniaDialogHost.cs 54.54% <ø> (ø)
Src/Common/FwAvalonia/CompactDialogStyles.cs 100.00% <ø> (ø)
... and 149 more

... and 8 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown

NUnit Tests

    1 files  ±0      1 suites  ±0   11m 30s ⏱️ +2s
5 763 tests +2  5 682 ✅ +2  81 💤 ±0  0 ❌ ±0 
5 772 runs  +2  5 691 ✅ +2  81 💤 ±0  0 ❌ ±0 

Results for commit 093ba5f. ± Comparison against base commit 1674067.

This pull request removes 4 and adds 6 tests. Note that renamed tests count towards both.
LexEdDllTests.EntrySequenceChooserCommandKeepOwnerActiveWhenHidingTests ‑ AddComplexFormChooserCommand_OptsIn
LexEdDllTests.EntrySequenceChooserCommandKeepOwnerActiveWhenHidingTests ‑ AddPrimaryLexemeChooserCommand_OptsIn
SIL.FieldWorks.Common.Framework.DetailControls.ChooserCommandKeepOwnerActiveWhenHidingTests ‑ MakeInflAffixEntryChooserCommand_OptsIn
SIL.FieldWorks.Common.Framework.DetailControls.ChooserCommandKeepOwnerActiveWhenHidingTests ‑ MakeInflAffixSlotChooserCommand_DoesNotOptIn
LexEdDllTests.EntrySequenceChooserCommandKeepOwnerActiveWhenHidingTests ‑ AddComplexFormChooserCommand_KeepsOwnerActive
LexEdDllTests.EntrySequenceChooserCommandKeepOwnerActiveWhenHidingTests ‑ AddPrimaryLexemeChooserCommand_KeepsOwnerActive
SIL.FieldWorks.Common.Framework.DetailControls.ChooserCommandKeepOwnerActiveWhenHidingTests ‑ MakeInflAffixEntryChooserCommand_KeepsOwnerActive
SIL.FieldWorks.Common.Framework.DetailControls.ChooserCommandKeepOwnerActiveWhenHidingTests ‑ MakeInflAffixSlotChooserCommand_KeepsOwnerActive
SIL.FieldWorks.XWorks.Search.BulkEditReplaceCharacterizationTests ‑ PreviewAndApply_ProduceTheSameTextAndWritingSystem
SIL.FieldWorks.XWorks.Search.BulkEditReplaceCharacterizationTests ‑ UnmatchedRow_IsDisabledAndHasNoPreviewReplacement

♻️ This comment has been updated with latest results.

johnml1135 and others added 2 commits August 14, 2026 05:19
Widens the diff-scoped gate from C#/PowerShell to also cover C/C++/IDL
(//, ///) and the <!-- --> comments in project files and Avalonia views,
per fieldworks-code-commenting. Adds a first-block doc-comment exemption
for Xml, mirroring /// and PowerShell's help block, since Xml has no
other syntax to mark a file/type-level summary -- without it, sixteen
dialog and project-file overview comments were losing real content to
the 200-character budget.

Fixes two real bugs found while doing this: the gate was scanning every
line of every changed file with Get-Content, ~50x slower than
File.ReadAllLines, plus a network round-trip in local base-ref
resolution via git remote show origin -- both fixed, dropping the gate
from 5.4s to ~2.2s. Separately, the XML spec forbids a literal "--"
anywhere in comment content, so the existing ASCII-punctuation
replacement (em dash to "--") produces invalid XML inside <!-- -->
comments; added a mechanical xml-illegal-double-hyphen check and use a
single "-" there instead.

Blamed the newly in-scope violations to their introducing commits: 87
trace to AI-authored commits and are fixed directly; the remaining
~5000 are pre-existing legacy debt, left for the diff-scoped ratchet to
grandfather like the existing C#/PowerShell backlog.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Audited every "bash" reference in the repo and git-blamed each one.
Nearly all traced to one commit, 5711bf6 ("enable AI workflows"),
which set the powershell skill's allowed-tools to Bash(pwsh:*) instead
of a native PowerShell tool -- the actual mechanism behind agents
defaulting to Bash across worktrees -- plus five .sh scripts
duplicating existing, already cross-platform-safe .ps1 equivalents.

Fixes: the skill now grants PowerShell directly; the five duplicate
.sh scripts are deleted (verified check-whitespace.sh and its .ps1
twin produce identical output first); the three ubuntu-latest CI
workflows (CommitMessage, check-whitespace, stray-docs) now run
shell: pwsh instead of bash, with the sed/GITHUB_ENV-heredoc logic
ported to PowerShell and verified locally; and ~44 bash-labeled doc
code fences for shell-agnostic commands are relabeled powershell.

Left alone: those workflows' ubuntu-latest runner choice, vendored
graphite2/update.sh, the 2018 Bin/list-assembly-references script,
WavConverter.cs's runtime /bin/bash call (real Linux code path, not
tooling), and one git rebase --exec example whose inner string must
stay POSIX syntax regardless of shell, since git always runs --exec
through its own bundled sh.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions

This comment has been minimized.

The build job failed: powershell-compat.ps1 still called
Get-CommentLineClassification with the old -IsPowerShell boolean
parameter, which the comment-hygiene scope-expansion commit replaced
with -Language. Missed because that call site was never exercised
locally this session -- only the module's own fixture suite was run,
not build.ps1 end to end. Fixed and verified directly under both
PowerShell engines.

The commit-message job failed separately: Tee-Object never creates
(or even truncates) its target file when the piped command emits zero
objects, unlike bash's tee, which always creates the file. A clean
gitlint run -- the common case -- produces no output, so
check_results.log never existed for the next step to read. Fixed by
pre-creating the file before the pipeline runs; verified the gap and
the fix directly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants