You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

5.7 KiB

status: current last-verified: 2026-10-10 owner: software-engineer source: Handoff 5 (the coverage unknowns): probes, red and green runs, and the independent review

Debugging insights

Recurring defect classes and how to find them. Evidence is in Tests/Coverage/Quality-Gate-Unknowns-2026-10-10.md; a fix needs a test that fails without it (Decision 16).

Culture-sensitive comparison (tr-TR)

  • With tr-TR, I and i are different letters, so a comparison that takes the culture of the thread drops or misses items: ToLower or ToUpper, Compare, StartsWith, EndsWith, or IndexOf(string) without a StringComparison, a case-insensitive Regex, a WildcardPattern without CultureInvariant (string.Equals(a, b) and == are ordinal and safe). Handoff 5 found two defects of this class: IdentityReference2.Equals(object) called with a name, which lowercased both sides (now OrdinalIgnoreCase), and Get-ChildItem2 -Filter (index* did not list Index.txt).
  • A scan of the C# is not enough: AlphaFS 2.2 builds a culture-sensitive regex from a search pattern. Get-ChildItem2 therefore enumerates under InvariantCultureScope (around the creation of the enumerator and each MoveNext, never across a WriteObject) and its own WildcardPattern has CultureInvariant. Check any new library call that takes a user pattern.
  • PowerShell -eq, -contains, and -in do not take the thread culture; only the .NET method call failed. Probe a claim before it enters the changelog or a report (the first wording named -eq and was wrong).
  • A test sets the culture of the thread, restores it in finally, names the rows that differ (index*, *.INI), keeps a control (INDEX*), reads the culture in the next pipeline stage for each item and once after the call (a cmdlet that does not restore it, or writes an item inside the scope, fails both: two mutations proved it), and is proved red on the previous build and green on the new one in all four configurations (Tests/ItemCmdlets.Tests.ps1, Tests/ObjectApis.Tests.ps1).

A test oracle that is wrong on a domain member

  • Get-Acl -Audit reads the SACL with the other sections and reports an item without audit entries as protected (Windows Server 2019, 2022, and 2025 domain members, Windows 11); the module and Get-NTFSInheritance read the SACL alone. Tests read the flag with .NET GetAccessControl(Audit) (Get-TestAuditAcl). A suite that is green on the host proves nothing about an oracle: run a new test on a domain member before calling it done.

A piped property and the parameter set

  • An object with a property AppliesTo, piped into Add- or Remove-NTFSAccess or -NTFSAudit with a path, bound the default PathComplex set and lost the property. Fixed in 93ed2c7: the four cmdlets declare no default set, so the property selects PathSimple, an object with AppliesTo and InheritanceFlags fails with AmbiguousParameterSet, and the tests pipe an object of each shape (red on the previous build, green on the new one). A probe that tells the sets uses an entry of the default scope: removing ThisFolderOnly from it leaves the InheritOnly part.
  • PowerShell restores only the parameters that the previous piped object bound. A field that the cmdlet writes itself stays for the next object: the scope that ProcessRecord converted from AppliesTo into the field behind -InheritanceFlags went to the next object without a scope. 93ed2c7 shipped that, the third review found it (rc7 had it for piped -SecurityDescriptor objects), and 9783d2a keeps the flags of a record in locals. A pipeline test pipes two or more objects of different shape in one call (Objects with different scopes in one pipeline, both ParameterSets files); one object per call cannot see it.
  • Probe the route that the documentation shows. Import-Csv of a file gives an empty string for a blank cell, ConvertFrom-Csv of strings gives $null for a trailing blank cell, and Import-Csv gives $null for a column that a row lacks. The binder treats them differently: $null fails to bind, and a value that it cannot convert to the enum (an empty string, a typo) is ignored, so the object is handled as one without the property. The first statement and the first tests used the convenient route, and the reviewer found the false claim; the corrected claim was then too narrow once more (a value that converts binds, whatever its form; a flag next to an unusable value wins). Write the claim from a probe of each form, not from the one that you tried.
  • A sentence that says what a test proves is a claim too: a title that says "and change nothing" needs an assertion that can see the change (the audit cmdlets write the SACL, not the DACL), and "cannot go red" needs a run.
  • The cause was DefaultParameterSetName = "PathComplex": when a piped object satisfies the mandatory parameters of the default set, PowerShell settles on it and never tries a parameter that belongs to another set. A stand-alone model with the four sets and no default binds PathSimple for an object with AppliesTo, PathComplex for one without, and an error for one with AppliesTo and InheritanceFlags (probes\p-parameterset-binding*.ps1 in the Handoff 5 evidence; the model binds PathComplex for a piped descriptor where the real cmdlets bind SDSimple); the real cmdlets agreed for paths (probes\p-proto-appliesto.ps1). The default dated from 4.2.6, when AppliesTo was optional; 4b1abc9 made it mandatory in the Simple sets, so the default no longer decided a named call. Build a model of the parameter sets before you change an attribute, and test the real cmdlet afterwards.