diff --git a/.memory-bank/activeContext.md b/.memory-bank/activeContext.md index 68e60ee..ba9f45c 100644 --- a/.memory-bank/activeContext.md +++ b/.memory-bank/activeContext.md @@ -1,6 +1,6 @@ --- status: current -last-verified: 2026-10-20 +last-verified: 2026-10-21 owner: active-agent source: current task evidence --- @@ -9,27 +9,56 @@ source: current task evidence ## Current focus -State at 2026-10-21: 5.0.0-rc8 is published and accepted. The -maintainer merged #126 (the decisions of 2026-10-19) and #127 (`c719197`: the -fix of the false "no access" of `Get-NTFSEffectiveAccess`, the known limit of -the hard-link cmdlets in the pages and the CHANGELOG, and the label `rc8`) and -tagged `5.0.0-rc8` at about 10:28Z. The CI run `39408140862` passed, and the -Gallery has rc8 since 10:40:58Z, GitHub since 10:41:07Z. The identity check -passed. The lab acceptance of the published bytes passed in one detached driver -(68 minutes, 10:38 to 11:47Z): three cells 1,374/0/12, the suite in 24 runs on -six machine classes, the first lab 245/0/1 per edition, every end state CLEAN -(record `Tests/Lab/Acceptance-2026-10-20-published-rc8.md`, #128). The boundary -of the new error, a computer name of 32,718 characters, holds on Server 2019, -2022, 2025, and Windows 11 26H1. He merged the record, #128, on 2026-10-21 at -05:48Z (`master` `461015e`, CI green). The tests-only round for the plain gaps -ran on 2026-10-20 (Next step 6) and is #129, with this Memory Bank in #130; -their CI is green, and the runner's counts equal the local run in each test -file. Open before the stable pull request: the merge of both, the maintainer's -decision on #34 on 2026-11-02 (no reply to his follow-up of 2026-10-19), and -his check of `PSGALLERY_API_KEY`. The stable build is new bytes that no lab -ran; the procedure accepts the last prerelease, so the stable change touches -only the label, the CHANGELOG, and `$publishedVersions`. Any other product -change needs rc9 and the same acceptance (about 70 minutes with the script). +State at 2026-10-21: 5.0.0-rc8 is published and accepted (tag `c719197`, CI run +`39408140862`, Gallery 2026-10-20 10:40:58Z, GitHub 10:41:07Z, identity check +passed; lab acceptance of the published bytes 10:38 to 11:47Z, record +`Tests/Lab/Acceptance-2026-10-20-published-rc8.md`, #128). `master` is +`d0cb57c`: the maintainer merged #128 (2026-10-21 05:48Z), the tests-only round +#129 (08:24Z), and the Memory Bank #130 (08:47Z). + +The last prerelease before the stable 5.0.0 is rc9 (Decision 27). The branch +`ai/rc9` (base `d0cb57c`, 36 commits, head `696ac38`) is complete and +validated; it is local until the maintainer pushes it, opens the pull request +(body: `C:\Temp\ntfs-rc9\final-msgs\pr-rc9-body.md`, temporary), merges it, and +tags `5.0.0-rc9`. It contains: the five piped parameters of `Add-` and +`Remove-NTFSAccess` and `-NTFSAudit` are `object`, converted in the cmdlet +(`ParameterConversion`), so a typo for `Deny` no longer creates an Allow entry; +a list for `-AccessType` and `-AppliesTo` is rejected; the `-SecurityDescriptor` +sets report a failing descriptor and go on; the 17 owner-restore handlers pass +on what a later command raises; `Get-ChildItem2 -Filter` has one matcher with +the Windows PowerShell 5.1 rules; the library fixes 3.1 to 3.3 and 3.10; +`Get-NTFSOrphanedAccess` writes an error; `-SecurityDescriptor` of +`Set-NTFSSecurityDescriptor` at position 1; tests that bind four parameters by +property name; label `rc9`. + +Validation of `696ac38`: the full suite in the four CI configurations, 2,162 +cases each, 0 failed (rc8: 1,615; skipped 27, 359, 58, and 390, so every new +case runs where it can); the pages and the help file are a fixpoint (0 drift, 0 +broken links; the help file is identical after a second generation); +PSScriptAnalyzer reports nothing in the 11 changed scripts; the public surface +differs from rc8 by 16 `object` properties, 5 completer classes, 1 protected +method, and 1 parameter position, and equals the candidate of round 1; a +matrix of valid shapes of the five parameters, run on the published rc8 and on +this build in both editions, differs in exactly the 9 list rows of +`Add-NTFSAccess`. Two review rounds (Decision 27, "Built in rc9"): round 1 on the +first integration found a Blocker (the last-dot rule of the filter) and two +Major findings; round 2 on the final tree and five checks of its fixes found +only Minor findings and Nits (wrong statements in the pages, completers that +were not culture invariant, the list for `-AccessType` and `-AppliesTo`, ways +around that rejection, a collection that holds itself, a message formatter that +threw, an enumerator that fails in `Dispose`); all are fixed with a test that +is red without the fix, or decided, and both reviewers closed their last +findings with "no finding" on `696ac38`. A collection that holds itself still +ends the host through the three flags parameters, as in rc8 (known limit). A +blank, `$null`, or `DBNull` value of the other parameter group is +`AmbiguousParameterSet` (documented, not restructured). + +Open before the stable pull request: the pull request, merge, and tag of rc9 +(maintainer); the lab acceptance of the published rc9 (agent, about 70 +minutes, `Chain-Published.ps1`); his decision on #34 on 2026-11-02; his check of +`PSGALLERY_API_KEY`. The stable change then touches only the label, the +CHANGELOG, and `$publishedVersions` (add `5.0.0-rc9`). The Memory Bank of this +work is a separate pull request (branch `ai/memory-bank-merged`). State at 2026-10-10: the maintainer integrated the release-gate stack into `master` (#116, #120, #118, #119, #121; `fa0701b`, CI green; GitHub closed #117 @@ -42,157 +71,67 @@ Decisions 22 and 24 on 2026-10-19 and decides 23 on 2026-11-02. ## Evidence -- rc6 Release run `37839669028`, attempt 2, succeeded; GitHub prerelease with - zip appeared 2026-10-09 07:01:34 UTC. The first attempt proves HTTP 409 - after Gallery publication, not the earlier assumed retry chronology. -- Integration on 2026-10-10 (UTC): #116 merged 09:10, #117 closed unmerged - 09:10:16 (events `base_ref_deleted`, `closed`), #120 merged 10:50, #118 - 11:12, #119 11:28, head branches deleted 11:34, CI on `master` at `fa0701b` - green 11:40. A `git merge-tree` simulation of the chain was conflict-free and - ended in the tree of the matrix branch. -- rc7 on 2026-10-10: tag at `fa0701b` about 12:20Z, CI run `38051611526` - green, Gallery 12:30:57Z, GitHub 12:31:09Z. `Test-PublishedRelease.ps1` - (12:33Z): tag commit on `master`, Gallery SHA-512 matches the nupkg, 11 - module files identical in nupkg and zip, manifest `5.0.0-rc7`; hashes in the - deployment notes, evidence in the session folder `published-rc7`. -- Gate 3 on the published rc7, 14:29 to 15:30 UTC (one detached driver; record - `Tests/Lab/Acceptance-2026-10-10-published-rc7.md`, evidence `gate3-rc7` with - a manifest): the three matrix cells (OSFile19, 22, 25 with OSWin11E, both - editions) 1,374 passed, 0 failed, 12 skipped, `LIVE_RESULT_VERIFIED` and - CLEAN in each; the suite in 24 runs on six machine classes without a failure - and with the host's skipped tests; the first lab with case 9 245 / 0 / 1 per - edition, CLEAN in four domains and on both machines. Each controller run - downloaded the package of the identity check (nupkg and DLL hashes equal). - Same counts as the final local candidate; no checkpoint was taken. -- Handoff 1 (paths), measured at `5a5d58b` (frozen Release, four configurations, - CI wrappers, then AltCover): 1,310 cases per configuration, zero failures - (baseline `3442194`: 914 cases); coverage 3,192/3,634 sequence points - (87.84%) and 1,273/1,978 branches (64.36%), against 74.21% and 50.39%. All 231 - methods with 442 unvisited points are classified: 223 explained, 8 open for - the maintainer. Skip eligibility was checked by row: all 578 skipped rows - (137 distinct tests) are executed in two other configurations. -- Eleven defects were fixed, ten with a guard that is red before the fix and - green after it (a red/green matrix over ten states of the branch: 76 rows - red at the base `f11ff41`, none after the last fix; the leak of a native - buffer has no guard). Mutation rounds at `5a5d58b`: 25 of 26 detected, M25 - (the check by type) an equivalent mutant. The matrix doesn't show that a test - preceded its fix: each fix commit carries both. Report: - `Tests/Coverage/Quality-Gate-Paths-2026-10-09.md`. +- Evidence of rc9 is outside git, in the session folder + `816cf47d-2f80-4eba-8ac9-41702c49560b\files\rc9`: the specs and review briefs, + `suite-full1` (first integration), `suite-final`, `suite-final2`, + `suite-final3`, `suite-final4`, `suite-final5`, `suite-final6-b436423`, and + `suite-final7-696ac38` (the head), the probes (`p-*.ps1`, + `fixb-probe`, `shapes-r2`, `Run-Oracle.ps1`, `Run-Pathological.ps1`), the + published rc8 (`rc8-published`), and the analyzer and surface results. The + review probes of the reviewers are in `C:\Temp\ntfs-rc9\review*-probes-*` + (temporary). The worktrees under `C:\Temp\ntfs-rc9` are temporary too. +- Milestones of earlier gates (rc6 and rc7 releases, handoff 1, the operating + system matrix, handoff 5, the rc7 and rc8 acceptances) are in `progress.md` + and in the records and reports under `Tests/Lab` and `Tests/Coverage`. - Reviews: the custom `security-reviewer` can't start (its model is - unavailable; no override). The built-in `code-review` agent made nine static - passes of the paths work (no Blocker or Major) and four rounds on the matrix - (one Major, record accuracy, resolved by the replay; Minors corrected); the - built-in `security-review` agent found no exploitable vulnerability and two - LOW items left for the maintainer (Next step 5). -- Live candidate of 2026-10-09 (09:20 to 09:51 UTC): 330 passed, 0 failed, 2 - expected skips; published rc6: 326 passed, 4 expected failures, 2 skips; the - 11 tested files match the ZIP. Independent probes verified the fixture - removal on six machines (`Tests/Lab/Acceptance-2026-10-09-quality-gate.md`). -- Six checkpoints report Standard even after a successful ProductionOnly probe; - policy restored, no restore performed: don't claim verified Production - rollback evidence. -- Lab acceptance of the paths fixes, 2026-10-09 20:41 to 21:42 UTC: candidate - `83149ee` against its base `f11ff41`, the same 244 tests per edition (78 - new): 486 passed, 0 failed, 2 expected skips against 338 passed, 148 failed - (each green on the candidate); fixture removal verified. Record: - `Tests/Lab/Acceptance-2026-10-09-quality-gate-paths.md`. -- Operating-system matrix, 2026-10-10 (record - `Tests/Lab/Acceptance-2026-10-10-os-matrix.md` with CSV tables): the suite of - the final candidate `fdd7a8b` passes on OSFile19/22/25, OSWin11E, OSWin11, and - the host (24 runs, no failure; the baseline `83149ee` fails 4 elevated and 20 - basic-user tests on OSFile22 and OSFile25); the live controller passes in - three cells (1,374 passed, 0 failed, 12 skipped) and in the first lab with - case 9 (245 passed, 0 failed, 1 skipped per edition; fixture removed and - verified). The Admin-role effective-access failures in earlier cells were - stale account state, not the module: in a replay the baseline failed two of - three cells and the candidate one, and one lifetime of about ten minutes fits - 43 runs of 27 cells (the Windows mechanism is unknown). The controller names - the case-3 account anew for each fixture (`1dec389`); four more cells with it - passed. A dry run of `Run-MatrixSequence.ps1 -Version 5.0.0-rc6` showed that - the published-package path works. Labs at 07:50 UTC on 2026-10-10: no fixture - or probe residue; `OSWin11E` shuts itself down an hour after its start. -- Handoff 5, the coverage unknowns, 2026-10-10 to 11 (report - `Tests/Coverage/Quality-Gate-Unknowns-2026-10-10.md`; raw evidence in the - session folder `816cf47d-2f80-4eba-8ac9-41702c49560b\files\qg-unknowns` - with a hash manifest). rc7 measured at `fa0701b` (four frozen - configurations, AltCover): 3,198/3,649 sequence points (87.64%), 1,090/1,168 - explicit branch points; 232 unvisited methods, all classified. Live suite on - an instrumented copy (first lab and three cells) and unit suite on - `OSFile25`: together +9 sequence and +4 explicit branch points (the code of - `fdd7a8b`); no test or probe reaches the audit ownership retry or the audit - read denial. 101 new `It` blocks; 7 of 64 parameter sets closed, 51 partly, - 1 open, 4 left to the maintainer. Cultures `de-DE` and `tr-TR`: two defects, - fixed. The piped - `AppliesTo` (Decision 26, debugging insights): fixed in `93ed2c7`; the - independent reviews then found a carry-over of the scope between piped - objects (`9783d2a`; rc7 had it for descriptor objects) and two false - sentences of the documents about empty values (`7bc7925`, `2112ee3`). Final - commit `2112ee3`, product code of `9783d2a` (its help file differs from the - package that the labs accepted): 1,492 cases per configuration, 3,266/3,668 - points, 183 unvisited methods, all classified. The labs accepted the package - (`ap7`, `ap8`): first lab 245/0/1 per edition; the 22 files on - `OSFile19/22/25` and `OSWin11` 1,189/0 in 16 configurations; the three cells - 229/0/2 per edition; all end states clean at 13:45Z. The first start of the - cells did nothing: a `powershell.exe` that PowerShell 7 starts inherits its - `PSModulePath` (lab README). Server Core, ARM64, a localized system, and - non-Windows servers are out of scope (Decision 25). -- rc8, 2026-10-20: fix (b) test-first on `ai/fix-effective-access-init-errors`. - A `-ServerName` of 40,000 characters gave `Synchronize` and no error before - and gives an error now; two tests were red and are green in four - configurations, and the suite has 1,494 cases per configuration, 0 failed. - The independent review (`code-review`) found one Minor, the warning "based on - this computer" before the error (fixed, with tests), and three Nits. The - acceptance evidence is `rc8-published` in the session folder - `816cf47d-2f80-4eba-8ac9-41702c49560b\files\qg-unknowns` (430 files, with a - manifest); the published hashes are in the deployment notes. -- Raw evidence is outside git: the session folder + unavailable; no override); the built-in `code-review` agent reviewed the paths + work, the matrix, rc8, and rc9; the built-in `security-review` agent found no + exploitable vulnerability and two LOW items for the maintainer (Next step 5). +- Raw evidence of the older gates is outside git: the session folder `4b12e2f4-d4c7-4a5d-883a-ddb7421c4848\files` (`qg-paths`, the matrix runs, the - night log) and - `26f151b6-e46f-49bd-9398-b27e20603949\files\quality-gate-3442194-20261009`. + night log), `26f151b6-e46f-49bd-9398-b27e20603949\files\quality-gate-3442194-20261009`, + and `816cf47d-2f80-4eba-8ac9-41702c49560b\files\qg-unknowns` (430 files of + `rc8-published`, with a manifest; the published hashes are in the deployment + notes). +- Six checkpoints of the lab report Standard even after a successful + ProductionOnly probe; policy restored, no restore performed: don't claim + verified Production rollback evidence. ## Next step -1. Gate 3 is done for the published rc7 and rc8 (the records). The stable build - is accepted through rc8 (Decision 21, "Closing the gate"). Open: keep or - replace the matrix VMs (about 60 GB, saved between runs) and the evaluation - client (it shuts down every hour; `OSWin11E` is off), and a domain cell for - Windows 11 26H1, whose client loses the secure channel to the Server 2025 - domain controller. `Chain-Published.ps1` in the session files runs the whole - sequence (not in the kit; deployment notes). +1. The maintainer pushes `ai/rc9`, creates the pull request, merges it after + CI, and tags `5.0.0-rc9` (commands in the reply of 2026-10-21; every release + step is his). Then run `Chain-Published.ps1 -Version 5.0.0-rc9 -Tag 5.0.0-rc9 + -Evidence ` from the session files (about 70 minutes, detached, with the + heartbeat; save the labs afterwards) and write the acceptance record + `Tests/Lab/Acceptance--published-rc9.md`. A defect that it finds needs + rc10. 2. The stable pull request: no `Prerelease`, `## [5.0.0] - ` with an empty - `[Unreleased]` and the links, and `5.0.0-rc8` in `$publishedVersions` (the - list ends at rc7 now). The maintainer checks the expiry of - `PSGALLERY_API_KEY` first, merges, and tags. Every release step is his. -3. He decides the other open items of the paths report: `FileSecurity` - conversions, `RemoveAll` account filters, lazy path overloads, abandoned - `PrivilegeEnabler`, dot patterns of `Get-ChildItem2 -Filter`, the 17 - owner-restore handlers without the later-command check, unused classes - (Decisions 21/22). Decisions 22 and 24 are accepted (2026-10-19). -4. #34 waits for a tester until 2026-11-02 (Decision 23, option 3; the - follow-up went up on 2026-10-19, no reply yet; checklist: + `[Unreleased]` and the links, and `5.0.0-rc9` in `$publishedVersions` (the + list ends at rc7 now; rc8 comes with the rc9 pull request). The maintainer + checks the expiry of `PSGALLERY_API_KEY` first, merges, and tags. +3. #34 waits for a tester until 2026-11-02 (Decision 23, option 3; checklist: `Tests/Lab/Non-Windows-File-Server-Test.md`). On that date the maintainer chooses between waiting longer and accepting the untested risk with a release-note caveat. No agent can accept it. -5. Of the two LOW findings of the security review (record, Limits), the - swallowed initialization errors of `GetEffectiveAccess` are fixed in rc8 - (`25fe775`, #127): an error instead of a false "no access", and the warning - only with a result. He still decides the other: the ACL of the stage - folders under `C:\` in the lab kit (they inherit Authenticated Users: - Modify; protecting them is a design change of the controller and needs a new - acceptance). The help could also say that a local standard user who asks - about a domain account still gets "Access is denied". -6. Handoff 5 is done and merged (Evidence). Decided on 2026-10-19 (Decision - 21): the hard-link names under another root stay a known limit; the pages - and the CHANGELOG say so since rc8. The tests-only round for the plain items - (68 `budget` mentions) ran on 2026-10-20: four implementers, one - `ParameterSets.*` file each, integrated on `ai/tests-round-1` (#129): 89 `It` - blocks, 121 cases, the suite 1,494 to 1,615 per configuration, 0 failed; - 38 of 64 sets closed, 26 with a leftover (16 Limit, 7 MaintainerDecision, 3 - would-pin), no defect; report - `Tests/Coverage/Quality-Gate-Test-Round-2026-10-20.md`. On the runner (CI run - `39551726767`) the counts equal the local run in each test file. No plain - gap is left. A defect that a test finds needs his decision and rc9. Still - his: the 7 MaintainerDecision and 3 would-pin rows, the silent default scope - for a piped `AppliesTo` that PowerShell cannot convert (a design change), the - 8 getters of pipeline parameters, and the audit paths. +4. Open housekeeping: keep or replace the matrix VMs (about 60 GB, saved between + runs) and the evaluation client (it shuts down every hour; `OSWin11E` is + off); a domain cell for Windows 11 26H1, whose client loses the secure + channel to the Server 2025 domain controller; the cleanup list before the + archive (AppVeyor, Read the Docs, wiki, `test/transfer`, old checkpoints) is + the maintainer's. The tests-round report still says that the new tests "have + not run on Windows Server 2019/2022/Windows 11"; the 24 lab runs of + 2026-10-21 changed that (fix in a later documentation pull request). +5. Of the two LOW findings of the security review, the swallowed initialization + errors of `GetEffectiveAccess` are fixed in rc8; he still decides the other: + the ACL of the stage folders under `C:\` in the lab kit (they inherit + Authenticated Users: Modify; protecting them is a design change of the + controller and needs a new acceptance). +6. Known limits after rc9, for the archive (Decision 27): `Get-FileHash2 + -Algorithm` keeps the silent default for a piped value that does not convert; + three settings still read their value with an unchecked cast; + `PrivilegeEnabler` is not disposed in some paths; `Get-NTFSEffectiveAccess` + fails with an `ArgumentOutOfRangeException` on a FAT32 volume or an item with + a NULL DACL (older than rc9); 9 of the 17 owner-restore handlers and the + error branch of `Get-NTFSOrphanedAccess` cannot be pinned by a test. 7. Do not release stable 5.0.0 or equate a percentage with gate closure. diff --git a/.memory-bank/debugging-insights.md b/.memory-bank/debugging-insights.md index 053c132..0bc4942 100644 --- a/.memory-bank/debugging-insights.md +++ b/.memory-bank/debugging-insights.md @@ -36,6 +36,11 @@ fails without it (Decision 16). 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`). +- The argument completers of rc9 had the same flaw (`WildcardPattern` with + `IgnoreCase` only: `THISFOLDERO` found nothing under `tr-TR`); the + second review found it, not the scan. A Turkish test word must put a capital + `I` against a lowercase `i` of the name: `NOPROPAGATEINH` faces the capital + `I` of `Inherit` and passes without the fix, so prove every row red first. ## A test oracle that is wrong on a domain member @@ -128,3 +133,89 @@ fails without it (Decision 16). gives up after 60 seconds when its runner never started (about 6 seconds normally). Before you blame the load for a stall, repeat the call alone with the same arguments. + +## Optional enum parameters that take pipeline input by property name + +- The binder skips a piped property that it cannot convert to the type of an + OPTIONAL parameter and keeps the default: a typo for `Deny`, a blank CSV + cell, or a misspelled scope silently made an Allow entry or the default + scope (probe `p-unconvertible-optional-params.ps1` on rc8, both editions; a + named call fails loudly; one parameter set with optional parameters does not + help). `Get-FileHash2 -Algorithm` has the same flaw and stays a known limit. +- Decision 27, fix B: the five parameters of the four add and remove cmdlets + are `object`, converted in the cmdlet by `ParameterConversion`. All five + must be `object`: with `AppliesTo` alone, a row with `AppliesTo` and flags + lost its `AmbiguousParameterSet`. The price was found by the review, not by + the author's tests: a `$null`, DBNull, or blank value of the OTHER group + binds too (a CSV with columns of both groups is `AmbiguousParameterSet`); a + named `$null` binds, where rc8 refused it (`MyInvocation.BoundParameters` + shows it in `BeginProcessing`); a script block is no longer delay-bound; the + completion of the names has to be rebuilt (quotes, wildcards, declaration + order); help shows `Object`. Probe every form of a value before you write a + claim, and ask a reviewer for the negative space. +- PowerShell accepts a comma list or an array for an enumeration that is not + `[Flags]` and combines the numbers bit by bit (a logical or): + `-AppliesTo 'ThisFolderOnly,FilesOnly'` made a `FilesOnly` entry and + `-AccessType 'Allow,Deny'` a `Deny` entry (4.2.6 and rc8 alike). The + conversion rejects a list for such an enumeration, in whatever kind of + collection it comes; a single name and a one-element collection stay valid. + A matrix of valid shapes, run on the published build and on the new one, + showed that exactly the 9 list rows changed. Code that opens a one-element + collection needs a depth limit: an array that holds itself hung the first + version (the binder of rc8 ends the host with a stack overflow on it), so + the conversion opens at most 32 levels and reads at most two elements. The + three flags parameters still hand such a collection to PowerShell's own + conversion, which ends the host with a stack overflow, as in rc8; not fixed + (no real input builds one, and a test would end the Pester process too). +- The message of a bad value is built inside a catch block, and a formatter + there must not throw: `Describe` called `ToString` of the one element outside + a guard, so an element whose `ToString` throws (a script method) ended a + piped command with an `ExtendedTypeSystemException` instead of one error for + that item (found by the last delta check of the second review; the version + before named a collection by its type and never formatted an element). Wrap + the whole formatter and fall back to the type name, in the short form + (`Type.ToString`; `FullName` spells out the assembly of every generic + argument). A test that pins a limit (ten elements named, two elements read) + is green on the code that it guards: prove it with a mutant of the limit (no + limit: 4 rows red; a limit of nine: 6 rows red). +- A loop that stops early runs `Dispose` of the enumerator, and `Dispose` can + fail: the catch of `TakeOneValue` returned the collection to PowerShell's + conversion, which combines a list bit by bit, so a sequence whose enumerator + throws in `Dispose` let a list through. The first patch (`isList = count > + 1` in the catch) fixed the reported shape (two elements) and left its + sibling (one element that holds a comma list); the closing check found it. + Fix the root: read with an explicit enumerator and release it in its own + guard, so that a failing release never changes what was read. The test needs + a compiled stand-in (`Add-Type`, as `PipelineControl.Tests.ps1` does); no + built-in enumerator fails in `Dispose`. +- PowerShell calls the getter of a by-property-name parameter once per + command, when a piped object first binds it (`p-getter-calls.ps1`). A test + that must visit it pipes an object that has the property. A property getter + that throws reads as `$null` without a trace: test it through reflection. + +## A differential oracle that covers part of the input + +- The matcher of `Get-ChildItem2 -Filter` was checked against Windows + PowerShell 5.1 over 55,987 patterns, on 8.3-compliant names only, so that the + short name is the real name. Such names have at most one dot, where the first + and the last dot are the same, so a rule about the LAST dot was invisible: + `*.??` dropped `archive.tar.gz`. The review found it with 849k pairs. Cover + every dimension of the input, and neutralize a quirk of the oracle instead of + avoiding the inputs that trigger it: `fsutil file setshortname` gives a long + name a short name that matches nothing, `fsutil 8dot3name strip` removes it. +- A very long pattern ended the host with a stack overflow (recursion depth = + tokens): bound the input of a recursive matcher (`ValidateLength`). + +## Agents in parallel worktrees + +- `git stash` is ONE list for all worktrees: a `pop` takes the top entry of + whoever created it and moved the edits of one agent into another's tree. The + brief forbids stash, `restore`, `reset`, `clean`, and checkout of a sha; a RED + run on the unchanged product uses a private base worktree. Protect a foreign + stash with `git update-ref` before anything else. +- An agent that ends its turn without a report may have done nothing (a clean + worktree and an empty probe folder said so): write to it and ask. +- Help generation: `Update-MarkdownHelp` reads `Position` from the help file + that the build copies, not from the C# attribute, so a page edit alone drifts: + edit the page, `New-ExternalHelp`, rebuild, then check the docs (the files are + a fixpoint). A pair of asterisks in one paragraph loses them in the XML. diff --git a/.memory-bank/decisions/0021-quality-gate-before-5.0.0.md b/.memory-bank/decisions/0021-quality-gate-before-5.0.0.md index ce4a81d..7557762 100644 --- a/.memory-bank/decisions/0021-quality-gate-before-5.0.0.md +++ b/.memory-bank/decisions/0021-quality-gate-before-5.0.0.md @@ -1,7 +1,7 @@ --- status: accepted date: 2026-10-08 -last-verified: 2026-10-20 +last-verified: 2026-10-21 owner: shared source: maintainer decision of 2026-10-08; closing choices of 2026-10-19; status of 2026-10-20 --- @@ -83,10 +83,10 @@ tests-only round ran the same afternoon (report `Tests/Coverage/Quality-Gate-Test-Round-2026-10-20.md`): 89 `It` blocks, 121 cases, the suite 1,494 to 1,615 per configuration with no failure, 38 of 64 parameter sets closed, 26 with a leftover (16 Limit, 7 MaintainerDecision, 3 -would-pin), no defect. It is #129; on the runner the counts equal the local -run in each test file. No plain gap is left. Open are the merge of #129, the -10 rows that wait for his decision (7 MaintainerDecision, 3 would-pin), #34 -(2026-11-02), and the key check. The stable build is new bytes: the stable -change may touch only the -label, the CHANGELOG, and `$publishedVersions`; any other product change needs -rc9 and the same acceptance. +would-pin), no defect. It is #129 (merged 2026-10-21); on the runner the +counts equal the local run in each test file. No plain gap is left. Open are +the 10 rows that wait for his decision (7 MaintainerDecision, 3 would-pin), +the decision on #34 (2026-11-02), and the key check. The stable build is new +bytes: the stable change may touch only the label, the CHANGELOG, and +`$publishedVersions`; any other product change needs rc9 and the same +acceptance. diff --git a/.memory-bank/decisions/0027-open-items-before-5.0.0.md b/.memory-bank/decisions/0027-open-items-before-5.0.0.md new file mode 100644 index 0000000..784d1dc --- /dev/null +++ b/.memory-bank/decisions/0027-open-items-before-5.0.0.md @@ -0,0 +1,242 @@ +--- +status: accepted +date: 2026-10-21 +last-verified: 2026-10-21 +owner: shared +source: maintainer answers of 2026-10-21 (fix scope B, items 3 to 7 change, review on; designs chosen in the third form); probes of 2026-10-21 on the published rc8 +--- + +# Decision 27: The open items before 5.0.0, and the piped optional parameters + +- Context: the tests-only round left no plain test gap (#129). On 2026-10-21 + the maintainer chose to fix the silent default scope of a piped `AppliesTo` + before 5.0.0 (rc9) and to go through the other open items one by one. rc9 + is the last prerelease: a change after its acceptance needs rc10, and after + 5.0.0 the repository is archived (Decision 18). So this is the last moment + to change any of the items below, and unchanged means accepted. +- Status: accepted. The scope and the designs are chosen (see "The + maintainer's choices", both tables). Every recommendation in the tables is + the agent's; the last column of the table of the other items stays empty, + because the choices are listed below. The details of fix B that the + maintainer did not see (error id, terminating or not) are the agent's design + and are flagged for the independent review. + +## New finding: the flaw is a family + +PowerShell skips a property of a piped object whose value it cannot convert to +the type of an optional parameter, and the parameter keeps its default. A named +call fails loudly; the same value on a piped object does not. Probe +`p-unconvertible-optional-params.ps1`, the published rc8, both editions (the +only difference is how `ReadData` prints): + +| Piped property | What rc8 does | +| --- | --- | +| `AccessType = 'Deny'` | A Deny entry, as intended | +| `AccessType = 'Denyy'` (typo), or a blank CSV cell | **An Allow entry**, no message | +| `AuditFlags = 'Failurre'` | Success and Failure are audited (the default), no message | +| `InheritanceFlags` or `PropagationFlags = 'Typo'` | Ignored; the default flags (`ContainerInherit`, `ObjectInherit`) | +| `AppliesTo = 'Typo'`, a blank cell, or `99` | Ignored; the broader default scope | +| `Remove-NTFSAccess` with `AccessType = 'Denyy'` | Looks for an Allow entry: the Deny entry stays, no message | +| `Get-FileHash2` with `Algorithm = 'SHA999'` | SHA256, no message | +| `-AccessType Denyy` as a named parameter | A binding error, as it should be | + +The decision of 2026-10-21 covers `AppliesTo` only. A typo for `Deny` that +creates an Allow entry is at least as serious, and it has the same cause. + +## Options for the fix + +Prototypes (`p-proto-appliesto-shapes.ps1`, `p-proto-object3.ps1`), both +editions, same attribute shape as `Add-NTFSAccess`: + +| Shape | Result | +| --- | --- | +| One parameter set with optional parameters | Does not help: the binder skips the unconvertible value there too | +| `AppliesTo` as `object` alone | Loud for a bad scope, but a CSV row with `AppliesTo` and `InheritanceFlags` is no longer `AmbiguousParameterSet`: the scope silently wins | +| `AppliesTo`, `InheritanceFlags`, `PropagationFlags`, and `AccessType` or `AuditFlags` as `object`, converted in the cmdlet with `LanguagePrimitives.ConvertTo` | Loud for every bad value (a non-terminating error for that object, the next object goes on, nothing carries over); `AppliesTo` with flags is `AmbiguousParameterSet` for every mix of strings and enums; valid values unchanged | + +| Option | What it is | Cost | +| --- | --- | --- | +| A | `AppliesTo` only (the decision as asked) | Leaves the Deny-to-Allow flip | +| B | The family in the four cmdlets (recommended): `AppliesTo`, `InheritanceFlags`, `PropagationFlags`, `AccessType` (access), `AuditFlags` (audit) | See below | +| C | B and `Get-FileHash2 -Algorithm` | B plus one cmdlet of low severity | +| D | No code change: pages and CHANGELOG say that piped values are not checked | Weak for a permissions tool | + +Consequences of B: the help shows `` for these parameters (the pages +list the values; an argument completer keeps tab completion); a named call +with a bad value is an error record of the cmdlet instead of a terminating +binding error; a blank cell for any of them is an error, not the default; a +`$null` `AccessType` stays the default (Allow); the pipeline conflict check is +now uniform. The work: tests first (the pins of the silent behavior change), +four cmdlets, four pages, the help file, the CHANGELOG, rc9, and the +acceptance (about 70 minutes with `Chain-Published.ps1`). An independent +review is recommended: public behavior and the permission scope. + +## The other open items + +| # | Item | What a user can notice | If changed | Recommended | Choice | +| --- | --- | --- | --- | --- | --- | +| 2 | The help says that a local standard user who asks about a domain account still gets "Access is denied" | Nothing changes in behavior | Docs only; rides with rc9 | Do it in rc9 | | +| 3 | Library-only items of the DLL that no cmdlet reaches: the `FileSecurity` conversions always throw; the `RemoveAll` filters ignore the account list (#113); the lazy path overloads; a `PrivilegeEnabler` that is not disposed stays enabled; the unused registry model and helper classes; `PipelineControl.IsEnd`; one unvisited catch in `Enable-Privileges` | Only .NET callers of the DLL | Code change and rc9 | Accept; list as known limits of the .NET API | | +| 4 | `Get-ChildItem2 -Filter` dot patterns differ from `Get-ChildItem` (`Report.*` misses `Report`; `Rep*.` returns nothing) | Documented, pinned by tests | New matching code and rc9 | Accept as documented | | +| 5 | The `-SecurityDescriptor` sets of the four add and remove cmdlets end with a terminating error when the change throws; the `-Path` sets write an error per item and go on (7 test rows stay unpinned) | A failing change to a descriptor in memory stops the cmdlet | Per-item handlers and rc9 | Accept | | +| 6 | Three small inconsistencies: the error category of `Get-NTFSAccess` after a failed ownership retry (`WriteError` there, `OpenError` elsewhere); `Get-NTFSOrphanedAccess` warns where its siblings write an error; `Set-NTFSSecurityDescriptor` takes `-Path` at position 2, its siblings at 1 | Scripts that check a category or pass a path by position | A breaking change for such scripts, and rc9 | Accept | | +| 7 | Seventeen owner-restore handlers do not pass on what a later command raises, in the rare case that it throws only for the `RestoreOwnerError` record | Rare | One check per handler, an elevated-only test, rc9 | Accept | | +| 8 | The ownership retry and the read denial of an audit write over SMB have no trigger on a Windows file server | None known | A seam (a design change), or the answer of a non-Windows server (#34) | Accept; #34 decides on 2026-11-02 | | +| 9 | Eight getters of piped parameters that no test binds by property name | None (tests only) | Tests only, no rc9 | Do it after the code of rc9 is final; the fix tests cover `AccessType`, `AuditFlags`, and `Algorithm` | | +| 10 | Lab and process only: the ACL of the lab stage folders under `C:\`, rollback checkpoints that report Standard, a publication recovery that never ran against the Gallery, test helpers that accept a link, the cleanup list (AppVeyor, Read the Docs, wiki, `test/transfer`, old checkpoints) | Not shipped | n/a | Accept; the cleanup before the archive is the maintainer's | | +| 11 | The deferred reviews of rc3 to rc7 (`progress.md`, open work 3) | Not new | n/a | Accept | | + +Decided before: the hard-link names under another root stay a documented known +limit (Decision 21); Decisions 22 and 24 are accepted; #34 is decided on +2026-11-02 (Decision 23). + +## The maintainer's choices, 2026-10-21 + +| Question | Answer | +| --- | --- | +| Scope of the fix of the piped optional parameters | B: the family in the four cmdlets (`AppliesTo`, `InheritanceFlags`, `PropagationFlags`, `AccessType`, `AuditFlags`) | +| Items to change in rc9 | 3 (library-only items), 4 (`Get-ChildItem2 -Filter` dot patterns), 5 (descriptor sets report per descriptor), 6 (three inconsistencies), 7 (17 owner-restore handlers) | +| Items accepted as recommended | 8 (audit retry and read denial over SMB; #34 decides on 2026-11-02), 10, 11. Items 2 and 9 need no decision: the help sentence goes into rc9, and the tests for the eight getters follow when the code of rc9 is final | +| Independent review of the rc9 change | Yes (`review: on`) | + +The design choices, third form, 2026-10-21 (each is the recommended default +except where noted): + +| Item | Choice | +| --- | --- | +| 3 | Fix 3.1 (`FileSecurity` and `DirectorySecurity` conversions), 3.2 (`RemoveAll` honors the account list), 3.3 (the path overloads write at once), 3.10 (malformed `EnablePrivileges` setting). Not fixed: 3.4 (`PrivilegeEnabler` never disposed: high risk, stays a known limit of the .NET API), 3.6 (registry model and helpers: kept, Decisions 21 and 22), 3.9 (`PipelineControl.IsEnd`: kept) | +| 4 | Option B: one matcher in the cmdlet with the Windows PowerShell 5.1 dot rules on the real names (8.3 short names stay ignored, invariant culture), in both editions | +| 6a | Keep `WriteError` for `ReadSecurityError` after a failed ownership retry, and pin it with a test | +| 6b | `Get-NTFSOrphanedAccess` writes an error for a read failure other than access denied: `ReadSecurityError`, `OpenError`, target the path | +| 6c | `Set-NTFSSecurityDescriptor -SecurityDescriptor` at position 1 (cosmetic) | +| B details | Not asked; the agent's design, see the row B below. `Get-FileHash2 -Algorithm` stays out (option C was not chosen) and is listed as a known limit | + +What follows: the work goes test-first into one branch, one commit per item, so +that any item can be dropped. A defect that the review finds is fixed before rc9 is tagged; a defect +that the acceptance finds needs rc10. One acceptance (about 70 minutes with +`Chain-Published.ps1`) covers rc9 as a whole. + +## Designs proposed, 2026-10-21 + +Five read-only research threads read the code, the tests, and the docs. The +sheet below is their result, verified in part by the probes above. Fix B is +decided; the rest is to be chosen. Recommended defaults are the agent's. + +| # | Design | Recommended | Alternatives and risks | +| --- | --- | --- | --- | +| B | `AppliesTo`, `InheritanceFlags`, `PropagationFlags`, `AccessType` (access cmdlets), `AuditFlags` (audit cmdlets) become `object` parameters; ProcessRecord converts them with `LanguagePrimitives.ConvertTo` into locals. One shared error: id `InvalidParameterValue`, category `InvalidArgument` (the category of the binder's own errors and of `AttributesEmpty`), message with the value, the parameter, and the valid names (a blank value has its own message). Revised before the build: a bad value on the command line stays a terminating error, as the binder made it (checked in `BeginProcessing`, so a `try`/`catch` around a named call still works); a bad value on a piped object is a non-terminating error per path (per descriptor in the `-SecurityDescriptor` sets), target that path, no item of that record is changed, and the next object goes on. A null `AccessType` or `AuditFlags` stays the default. Argument completers (`IArgumentCompleter`, available in the referenced Windows PowerShell 5.0 assembly) keep tab completion. Docs: `Type: Object`, help from `New-ExternalHelp`, the CHANGELOG paragraph at lines 247 to 258 | As described | `Get-FileHash2 -Algorithm` has the same flaw (option C, not chosen). The error of a named call now comes from the cmdlet, not the binder (other id, same terminating effect) | +| 5 | In the four `-SecurityDescriptor` branches, a `try/catch (Exception)` around the change of each descriptor writes `AddAceError` or `RemoveAceError`, category `WriteError`, target the descriptor, message of the exception, and goes on; the later-command check of item 7 goes into the same catch. Lines: `AddAccess.cs` 176 to 184, `RemoveAccess.cs` 177 to 185, `AddAudit.cs` 175 to 185, `RemoveAudit.cs` 176 to 186 | As described | Tests: a failing descriptor followed by a valid one; the 7 unpinned rows | +| 7 | One `IsFromLaterCommand(ex)` check, rethrowing, in each of the 17 outer handlers (the pattern of `SetSecurityDescriptor.cs` 72 to 101): `AddAccess`, `RemoveAccess`, `ClearAccess`, `GetAccess`, `GetOrphanedAccess`, `GetEffectiveAccess`, `AddAudit`, `RemoveAudit`, `ClearAudit`, `GetFileHash2`, the six inheritance cmdlets, and `SetSecurityDescriptor` | As described | Tests: one parameterized elevated-only test (a later command that throws on the `RestoreOwnerError` record), like the existing `restoreOwnerCases` | +| 3.1 | `FileSystemSecurity2` conversions from `FileSecurity` and `DirectorySecurity` keep the given descriptor | Fix | Remove the operators: breaking public API. Medium risk | +| 3.2 | `RemoveFileSystemAccessRuleAll` and `RemoveFileSystemAuditRuleAll` honor the account list | Fix | High risk for external callers (they remove entries); the cmdlets pass no list. Tests for both rule types | +| 3.3 | The path and several accounts overloads of the two add methods write at once, like their siblings | Fix | Medium risk (the moment of an exception changes) | +| 3.4 | `PrivilegeEnabler` no longer keeps an undisposed instance reachable | Defer unless the maintainer insists | High risk: token state in every cmdlet; a finalizer test is not deterministic; every cmdlet disposes its enabler today | +| 3.6 | The registry model (about 500 lines), `FileSystemFlags`, `FileSystemEffectivePermissionEntry`, and two raw descriptor readers | Keep and document (Decisions 21 and 22) | Removal breaks unknown .NET callers of the public DLL | +| 3.9 | `PipelineControl.IsEnd` | Keep as defense in depth | A change touches the pipeline semantics of all cmdlets for no known defect | +| 3.10 | The malformed `EnablePrivileges` setting: one validation in the base class, the dead catch removed | Fix | Medium risk (base class) | +| 4 | `Get-ChildItem2 -Filter`. A: pass the pattern unchanged to AlphaFS (the Windows PowerShell 5.1 rules; brings back the 8.3 short-name matches that a past fix removed). B: one cmdlet-side matcher with the Win32 dot rules on the real names and a broad enumeration (about 40 lines; costs a larger enumeration over SMB). D: keep as documented | B | Even `Get-ChildItem` differs between the editions here, so "aligned" needs a stated policy: the Windows PowerShell 5.1 dot rules on real names, in both editions | +| 6a | `Get-NTFSAccess`, the category of `ReadSecurityError` after a failed ownership retry: `WriteError` in `Get-NTFSAudit`, `Get-NTFSEffectiveAccess`, `Get-NTFSInheritance`, and `Get-NTFSSecurityDescriptor` too, so it is the majority convention | Keep, and pin it with a test | `OpenError` would break scripts that check the category, and make the module inconsistent unless six cmdlets change | +| 6b | `Get-NTFSOrphanedAccess` warns for a read failure other than access denied | An error: `ReadSecurityError`, `OpenError`, target the path | Breaks `-WarningAction` users; `-ErrorAction Stop` newly stops | +| 6c | `Set-NTFSSecurityDescriptor` has `-SecurityDescriptor` at position 2 and no other positional parameter | Position 1 | Cosmetic: a lone positional parameter binds first whatever its number | + +## Assumption flagged for the maintainer's review + +- The lab suite: he answered "not sure, you pick". The agent took the + recommendation: run the full suite (1,615 cases per configuration) on the + lab machine classes now, against the published rc8, so that an OS-specific + finding can still go into rc9 instead of forcing rc10. It changes nothing in + the repository or the product. Evidence: `suite-newtests` in the session + folder. Result (2026-10-21, 09:10 to 09:33 UTC): 24 runs on six machine + classes (Windows Server 2019, 2022, and 2025, Windows 11 22H2 and 26H1, and + the host), 0 failures. Every run gained exactly 121 cases against the rc8 + acceptance, and the four new files give the counts of the CI runner (elevated + Desktop: Access 74/0, Audit 55/2, Inheritance 72/1, Items 90/0, executed and + skipped). No OS-specific finding. The machines are saved again, and + `OSWin11E` is off. + +## Built in rc9, the review, and the known limits + +- How: four implementers worked in separate worktrees (WI-1 the five piped + parameters and item 5, WI-2 item 7 and the three inconsistencies, WI-3 the + library items, WI-4 the filter), test first, one commit per item. The agent + integrated them, ran the whole suite on the integrated tree, and had three + read-only reviewers read it (brief `rc9\REVIEW-BRIEF.md` in the session + folder). The findings became follow-up commits, and the branch `ai/rc9` was + rebuilt with `git cherry-pick` in the order of each chain, so every fix sits + in the commit of its item and any item can still be dropped. +- Round 2 (brief `rc9\REVIEW-BRIEF-2.md`): two reviewers read the final tree + (scope A the four add and remove cmdlets, scope B everything else), then + checked the fixes of their own findings. No Blocker and no Major; nine Minor + findings: five wrong or incomplete statements in the pages and the CHANGELOG + (a script block "was silently ignored" when it was evaluated per object; + `-AppliesTo $null` keeps the binder's error; the carry-over of the scope + affected only objects with one flags property; the descriptor sets that go + on after a failing descriptor are a breaking change; the PowerShell 7 list of + patterns was too narrow), the trailing blank and `..` of the filter + (documented, not changed), a stale header of two test files, the completers + without `CultureInvariant` (fixed), and a list for `-AccessType` and + `-AppliesTo` whose numbers PowerShell combines bit by bit (the maintainer + chose on 2026-10-21 to reject it in rc9; older than rc9, the same in + 4.2.6). Two more checks of the fixes found that the rejection could be + bypassed through other kinds of collection (a hash set, a queue, the keys of + a dictionary, a LINQ result, a one-element collection that holds a comma + string), that a collection that holds itself never returned (the binder of + rc8 ends the host with a stack overflow on it), and that the message named + the type, not the typo: fixed in `e39aeed` and `da41a8e`, each with tests + that were red first (32 and 28 rows). The last two checks found four Nits in + code that those fixes added or kept: the formatter of the message threw when + the text of an element throws (a terminating error that stopped a piped + command), its fallback named a generic type with the assembly of every type + argument, and an enumerator that throws in `Dispose` turned a list into a + value that PowerShell converts (first for two elements, then for one element + that holds a list): fixed in `ec58a18`, `ce34395`, `b436423`, and `696ac38`, + with 24, 2, 4, and 8 rows that were red first, and the pins of the limits + proved with two mutants. The last commit fixes the root, + not the shape: the collection is read with an explicit enumerator that is + released in its own guard, so a failing release changes nothing of what was + read. Final suite on `696ac38`: 2,162 cases per configuration, 0 failed in + four configurations (rc8: 1,615); the pages and the help file are a + fixpoint; PSScriptAnalyzer finds nothing in the 11 changed scripts; the + public surface equals the validated candidate; a matrix of valid shapes + differs from the published rc8 in exactly 9 list rows; both reviewers closed + their findings with "no finding". + +What the review found: + +| Finding | What became of it | +| --- | --- | +| Blocker: a `*` before a dot has to stop at the LAST dot, the matcher stopped at the first; `*.??` missed `archive.tar.gz`. The differential check ran on 8.3 names, which have one dot, and could not see it | Fixed; a differential check over 732,630 multi-dot pairs found no mismatch; the tests pin the multi-dot shapes | +| Major: with `object` parameters, a `$null`, DBNull, or blank value of the OTHER parameter group binds in both sets, so a CSV row with a blank cell of the other group is `AmbiguousParameterSet` (rc8 skipped the cell and created the entry) | Documented and pinned. **Flagged:** the alternative is a restructure of the parameter sets, a larger public change; the maintainer was told that the agent goes on with the documentation and has not objected | +| Major: a named `$null` (`-AccessType $rule.AccessTpye`) silently selected the default | Fixed: a terminating `InvalidParameterValue` for a name in `BoundParameters` whose value is `$null` | +| Minor: a script block is no longer delay-bound; the completers returned nothing for a quoted or wildcard word | Documented and pinned; the completers are fixed | +| Minor: since 3.1, `-SecurityDescriptor (Get-Acl $f)` binds, and then threw from the inherited-from lookup and marked the audit section as read; the null guard of `Name` was not pinned; `Get-NTFSInheritance` wrote its object in a `finally` after it rethrew the exception of a later command | Fixed; the guard is pinned through reflection, because a PowerShell property read turns a throwing getter into `$null` | +| Filter: platyPS removed paired asterisks of a paragraph; the PowerShell 7 claim was too broad; the CHANGELOG said nothing of the new `?`; a pattern of more than 32,767 characters ended the host with a stack overflow; one sentence contradicted itself | Fixed: one asterisk per paragraph, the exact shapes, the CHANGELOG sentence, `[ValidateLength(0,32767)]` | + +Known limits, kept and to be stated to the maintainer: + +- `Get-NTFSEffectiveAccess` on a FAT32 volume or on an item with a NULL DACL + ends with an `ArgumentOutOfRangeException` (mask 2097151). Found by a probe, + not caused by rc9, not fixed. +- The error branch of 6b (`Get-NTFSOrphanedAccess` cannot read an item) has no + deterministic trigger: a locked file, a long path, a dangling junction, and a + FAT32 VHD were tried. It is reviewed, not pinned. +- Item 7: 9 of the 17 handlers have no test. The getters' own + `FileSystemOwner.GetOwner` needs the same read right that the test takes + away, and the audit cmdlets write the SACL whatever the DACL says, so the + action never fails first. +- `Get-FileHash2 -Algorithm` keeps the silent default of a piped value that + does not convert (option C was not chosen). The settings `GetInheritedFrom`, + `GetFileSystemModeProperty`, and `IdentifyHardLinks` still read their value + with an unchecked cast, as `EnablePrivileges` did. `PrivilegeEnabler` (3.4) + stays undisposed, a known limit of the .NET API. +- In PowerShell 7, the case folding of `-Filter` differs from NTFS for a few + exotic letters (finding F7 of the review; not fixed). +- A collection that holds itself, given to `-InheritanceFlags`, + `-PropagationFlags`, or `-AuditFlags`, ends the host with a stack overflow + inside PowerShell's own conversion, exactly as in rc8 (both reviewers + reproduced it in both editions, without the module too: any typed parameter + or cast does it). `-AccessType` and `-AppliesTo` reject it. Not fixed: no + real input builds such an object, a guard for the flags parameters would + need a visited-set walk of every argument, and a regression test would end + the Pester process before the fix. Named in the pull request text, not in + the CHANGELOG (user-visible changes only). diff --git a/.memory-bank/progress.md b/.memory-bank/progress.md index 1d10c51..045b56c 100644 --- a/.memory-bank/progress.md +++ b/.memory-bank/progress.md @@ -1,6 +1,6 @@ --- status: current -last-verified: 2026-10-20 +last-verified: 2026-10-21 owner: active-agent source: repository and validation evidence --- @@ -16,53 +16,32 @@ into `master` (CI green; the milestones have the details). On 2026-10-19 he merged #125 (Handoff 5) and #124 (Dependabot) and decided the path to stable (Decision 21, "Closing the gate"); on 2026-10-20 he merged #126 (the decisions) and #127 (the rc8 fix and label) and tagged rc8; on 2026-10-21 he merged #128 -(the rc8 record). The published rc7 and rc8 passed their lab acceptance in the -matrix and in the first lab. Stable Gallery version: 4.2.6. -After 5.0.0, archive in favor of WindowsAccessControl (Decision 18). +(the rc8 record), #129 (tests), and #130 (this file). The published rc7 and +rc8 passed their lab acceptance in the matrix and in the first lab. rc9 +(Decision 27) is built on the local branch `ai/rc9` and waits for his push, +pull request, merge, and tag. Stable Gallery version: 4.2.6. After 5.0.0, +archive in favor of WindowsAccessControl (Decision 18). ## Recent milestones -- 2026-10-02 to 2026-10-06: documentation/help aligned with source, CI and - wiki moved to GitHub Actions, versioning/release automation established, - and prereleases rc1 to rc4 published. Earlier detail is in git, - `CHANGELOG.md`, `Docs/Version-History.md`, and Decisions 1 to 19. -- 2026-10-07: lab comparison of rc2/rc4 reproduced #34 over SMB and proved - changed-section writes preserve the owner. Remote effective-access - fallback returned no result; fixed test-first for rc5 (Decision 20). -- 2026-10-08: #114 merged (`fcb370e`), rc5 published and live-tested. - Decision 21 established the quality gate. Corrected four-run coverage: - rc5 58.1% sequence points/38.0% branches, not the initial one-run result. -- 2026-10-08: rc6 Phase 2 added basic-user CI, parameter-set/error tests, - expanded domain/SMB cases, and fixes for privilege cleanup, path - resolution, ownership retries, item conflicts, output equality, and - inherited flags. Suite: 677; coverage 68.14% sequence/44.32% branches. - Lab acceptance of `7b0781f` passed; two independent review passes. -- 2026-10-08: rc7 implemented proposed Decision 22, including breaking - link binding/error changes, SimpleAccess traversal, cross-volume folder - preservation, and named effective-access warnings. Suite: 712, no - failures or test skipped everywhere. One review: no Blocker/Major. - Lab candidate `dc6e9f5`: 326 passed, 2 skipped, cleanup verified. -- 2026-10-08: #115 merged (`b51d970`) and tagged rc6. Release run - `37839669028` failed with HTTP 409 after Gallery publication. The log - does not establish the previously assumed initial timeout/retry cause. - Published rc6 live tests differed only in rc7's warning expectation. -- 2026-10-09: release attempt 2 succeeded; rc6 GitHub prerelease and zip - appeared at 07:01:34 UTC. #116 passed CI on `d25647d`. -- 2026-10-09: autonomous follow-up on `ai/quality-gate-coverage` (#117, then - #120), through `3442194`: 202 cases above rc7; fixed rooted result paths and - the first-hidden-item omission; publication recovery verifies the exact - SHA-512 identity, not merely the version. Suite: 914 per configuration, zero - failures; 2,641/3,559 sequence points (74.21%), 974/1,933 branches (50.39%). - Live comparison: candidate 330 passed, 0 failed, 2 expected skips (`7594e0c`). -- 2026-10-09: handoff 1 (#118): suite 914 to 1,310 per configuration, zero - failures; 3,192/3,634 sequence points (87.84%), 1,273/1,978 branches; all 231 - unvisited methods classified (223 explained, 8 open). Eleven defects fixed, - ten with a guard that is red before the fix and green after it (76 rows red - at the base), among them a later command's exception that cmdlets swallowed - (a `throw` made `Remove-Item2` remove the next item) and a privilege left - enabled. Lab acceptance, `83149ee` against `f11ff41`: 486 passed, 0 failed, 2 - expected skips against 338 passed, 148 failed. Report in `Tests/Coverage`; - record `Tests/Lab/Acceptance-2026-10-09-quality-gate-paths.md`. +- 2026-10-02 to 2026-10-09: documentation and help aligned with the source, CI + and wiki on GitHub Actions, release automation, and rc1 to rc6 published + (rc5: the remote effective-access fallback, Decision 20; rc6 and the rc7 + candidate: Phase 2 and the behavior changes of Decision 22). #114 (`fcb370e`) + and #115 (`b51d970`) merged. The rc6 release run failed with HTTP 409 after + Gallery publication and attempt 2 succeeded (GitHub prerelease 2026-10-09 + 07:01:34Z); the log does not establish the earlier assumed timeout cause. + Detail: git, `CHANGELOG.md`, `Docs/Version-History.md`, Decisions 1 to 22. +- 2026-10-09: the autonomous follow-up (#117, then #120, through `3442194`) and + handoff 1 (#118) took the suite from 202 cases above rc7 to 1,310 per + configuration with zero failures and 3,192/3,634 sequence points (87.84%; + 74.21% before); publication recovery verifies the exact SHA-512 identity. + Eleven defects were fixed, ten with a guard that is red before the fix (76 + rows red at the base), among them a later command's exception that cmdlets + swallowed (a `throw` made `Remove-Item2` remove the next item) and a privilege + left enabled. Lab acceptance, `83149ee` against `f11ff41`: 486 passed, 0 + failed against 338 passed, 148 failed. Report in `Tests/Coverage`; record + `Tests/Lab/Acceptance-2026-10-09-quality-gate-paths.md`. - 2026-10-09 to 10: handoffs 2 to 4 under the maintainer's delegation (decisions D1 to D47 in the night log of the session files). The matrix lab `NtfsSecurityOsMatrixLab` (Server 2019, 2022, 2025, a Windows 11 Enterprise @@ -129,13 +108,31 @@ After 5.0.0, archive in favor of WindowsAccessControl (Decision 18). of 32,718 characters). Record `Tests/Lab/Acceptance-2026-10-20-published-rc8.md` (#128). - 2026-10-20: tests-only round (Decision 21). Four implementers, one - `ParameterSets.*` test file each; integrated on `ai/tests-round-1` (#129): - 89 `It` blocks, 121 cases, the suite 1,494 to 1,615 per configuration, 0 - failed in four configurations; 38 of 64 sets closed (7 before), 26 with a - leftover (16 Limit, 7 MaintainerDecision, 3 would-pin); no defect. Report: - `Tests/Coverage/Quality-Gate-Test-Round-2026-10-20.md`. On 2026-10-21 the - CI of #129 and #130 passed; the runner's counts equal the local run in each - test file (run `39551726767`). + `ParameterSets.*` test file each (#129): 89 `It` blocks, 121 cases, the + suite 1,494 to 1,615 per configuration, 0 failed in four configurations; 38 + of 64 sets closed (7 before), 26 with a leftover (16 Limit, 7 + MaintainerDecision, 3 would-pin); no defect. Report: + `Tests/Coverage/Quality-Gate-Test-Round-2026-10-20.md`. On 2026-10-21 he + merged #129 (08:24Z) and #130 (08:47Z); the new tests passed in three CI + runs and in 24 lab runs on six machine classes (0 failed). A probe then + found that piped optional parameters ignore a bad value (a typo for Deny + creates an Allow entry); Decision 27 sets the scope of rc9, the designs were + chosen on 2026-10-21, and four work items started (branch + `ai/rc9-before-stable`). +- 2026-10-21: rc9 built and reviewed (Decision 27; branch `ai/rc9`, 36 commits + over `d0cb57c`, head `696ac38`, local until he pushes). Four implementers + worked test-first in worktrees (the five piped parameters, the 17 handlers + and three inconsistencies, the library items, the filter); the chains were + cherry-picked in order, so every fix sits in the commit of its item. Round 1 + (three read-only reviewers) found a Blocker (the last-dot rule of the + filter) and two Major findings (a named `$null`; a blank value of the other + parameter group, documented); round 2 (two reviewers, five checks) found + only Minor findings and Nits, among them completers that were not culture + invariant (`tr-TR`) and a list for `-AccessType` or `-AppliesTo`, which + PowerShell combines bit by bit; he chose to reject it. Final suite on + `696ac38`: 2,162 cases per configuration, 0 failed in four configurations + (rc8: 1,615); docs fixpoint; analyzer clean; valid shapes differ from rc8 in + 9 list rows only; both reviewers: no finding. ## Stable capabilities @@ -149,13 +146,13 @@ After 5.0.0, archive in favor of WindowsAccessControl (Decision 18). ## Open work 1. Decision 21 gate: the published rc7 and rc8 are accepted in the matrix and - the first lab (records); stable follows rc8 (Decision 21, "Closing the - gate"). Do not release 5.0.0 until the other gates close: the merge of #129 - and #130 (no plain gap is left), #34 on 2026-11-02, and the key check. - Release steps: - `Docs/Contributing/05-Releasing.md`; remove the prerelease label, date - `[5.0.0]`, add the last prerelease (rc8) to `$publishedVersions` (never the - version of the manifest), tag through CI. + the first lab (records); stable follows rc9 (Decision 27): after his push, + pull request, merge, and tag of rc9 the agent runs the lab acceptance of + the published bytes (about 70 minutes). Do not release 5.0.0 until the other + gates close: that acceptance, the decision on #34 on 2026-11-02, and the key + check. Release steps: `Docs/Contributing/05-Releasing.md`; remove the + prerelease label, date `[5.0.0]`, add the last prerelease (rc9) to + `$publishedVersions` (never the version of the manifest), tag through CI. 2. Issues: #110's seven items were addressed by rc6, but #115 deliberately used no closing keyword. #34 stays open for non-Windows owner feedback or maintainer acceptance. #16, #21, #45, #89 await reporters. #68 tracks @@ -177,10 +174,9 @@ After 5.0.0, archive in favor of WindowsAccessControl (Decision 18). (`2112ee3`): 402 unvisited sequence points in 183 methods, all classified (`Tests/Coverage`): 167 explained from source with evidence, 16 open (8: `FileSecurity` conversions, `RemoveAll` account filters; 8 getters of - pipeline parameters that no test binds by property name). Other open - decisions: lazy path overloads, abandoned `PrivilegeEnabler`, dot patterns - of `Get-ChildItem2 -Filter`, 17 owner-restore handlers that do not pass on - what a later command raises (a rare combination), unused classes. No + pipeline parameters that no test binds by property name). Decision 27: rc9 + fixes the conversions, `RemoveAll`, the lazy overloads, the dot patterns, + and the 17 handlers; `PrivilegeEnabler` and unused classes stay. No trigger for an audit write's ownership retry was found on a local volume or a Windows file server (probe of 48 calls) and no test reaches it. A conditional ACE display remains a .NET representation limit, not evidence diff --git a/.memory-bank/systemPatterns.md b/.memory-bank/systemPatterns.md index 8f7d292..0da3a5d 100644 --- a/.memory-bank/systemPatterns.md +++ b/.memory-bank/systemPatterns.md @@ -1,6 +1,6 @@ --- status: current -last-verified: 2026-10-19 +last-verified: 2026-10-21 owner: active-agent source: repository and regression evidence --- @@ -15,13 +15,10 @@ source: repository and regression evidence | `NTFSSecurity.Init.ps1` | Loads Security2/privilege assemblies and prepends formatting | | `NTFSSecurity.dll` | 36 PowerShell cmdlets; BaseCmdlet path/privilege behavior | | `Security2.dll` | DACL/SACL objects, owners, inheritance, effective access, Win32 | -| AlphaFS | Long-path files/directories/links | -| PrivilegeControl / ProcessPrivileges | Token privilege operations | +| AlphaFS, PrivilegeControl, ProcessPrivileges | Long-path files/directories/links; token privilege operations | | `en-US/NTFSSecurity.dll-Help.xml` | Committed help generated from cmdlet Markdown | -## Decisions - -Read only task-relevant records; the index controls routing. +## Decisions (read only the task-relevant records; the index routes) | # | Decision | | --- | --- | @@ -51,6 +48,7 @@ Read only task-relevant records; the index controls routing. | 24 | [The operating-system matrix lab](decisions/0024-os-matrix-lab.md) | | 25 | [Four environments are out of scope for the tests](decisions/0025-out-of-scope-environments.md) | | 26 | [No default parameter set for Add and Remove (access, audit)](decisions/0026-no-default-parameter-set-add-remove.md) | +| 27 | [The open items before 5.0.0 and the piped optional parameters (rc9)](decisions/0027-open-items-before-5.0.0.md) | ## Patterns @@ -67,36 +65,38 @@ Read only task-relevant records; the index controls routing. attempts all cleanup, and preserves explicit enables. Dispose has no stream. - Pipeline getters never throw; per-item errors name input and allow continuation. - Folder moves never use CopyAllowed; preserve cross-volume source folders. -- Apply implied Hidden/Force before deciding to emit, including the first item. + Apply implied Hidden/Force before deciding to emit, including the first item. - A catch-all for one item's failures passes on what a later command raises - through a Write call (`IsFromLaterCommand`, `PipelineControl`; see - `BaseCmdlets.cs`); add a row to `Tests/PipelineControl.Tests.ps1` for a new - cmdlet. Record an enabled privilege before the next write. -- `Get-ChildItem2 -Filter` matches culture invariant; `*.*` means `*`. + through a Write call (`IsFromLaterCommand`, `BaseCmdlets.cs`); add a row to + `Tests/PipelineControl.Tests.ps1` for a new cmdlet. Record an enabled + privilege before the next write. +- `Get-ChildItem2 -Filter`: one matcher, 5.1 DOS wildcard rules (Decision 27). +- Optional by-name pipeline parameters are `object` + `ParameterConversion` (Decision 27). ### Tests and documentation - Tests import Release in isolated processes, both editions and privilege modes, with guarded sandboxes; a skip needs an eligible counterpart. -- Assert persisted state, errors/targets, continuation, and no failed - PassThru output. Prove new characterization guards with bounded mutations - (one round together only if no guard can fail because of another); restore - source exactly and rebuild before green validation or packaging. A retry - test asserts its precondition (the plain write is denied). +- Assert persisted state, errors/targets, continuation, and no failed PassThru + output. Prove characterization guards with bounded mutations (together only + if no guard can fail because of another); restore source and rebuild before + green runs or packaging. A retry test asserts its precondition (the plain + write is denied). - CI scripts and focused runs set `Stop`: a test that needs a non-terminating error names `-ErrorAction Continue`. Don't name a test variable like an automatic variable (`$foreach`): Pester runs the block inside a foreach. - Fixture DACLs use .NET SetAccessControl, not Set-Acl's unintended SACL writes. - Scope/descendant expectations are independent of the production converter. - Desktop platyPS: generate help, rebuild, round-trip unchanged, check links; - platyPS 0.14.2 turns paired asterisks into emphasis, even in backticks. + 0.14.2 turns paired asterisks into emphasis, even in backticks, and + `Update-MarkdownHelp` reads `Position` from the committed help file. - Live tests use only approved lab targets, SMB then independent server state; Get/SetFileSecurity preserves stored DACLs; rights oracles use S4U tokens. - A suite that is green on the host and CI misses defects that need a domain member or another token (the matrix found three): also run a domain member, other builds, and a basic user; a failure that follows cell order points to - stale account state (Decision 24). Oracles differ there too: `Get-Acl -Audit` - reads an item without audit entries as SACL-protected; read the SACL alone. + stale account state (Decision 24). `Get-Acl -Audit` reads an item without + audit entries as SACL-protected; read the SACL alone. ### CI, publication, and integration diff --git a/.memory-bank/techContext.md b/.memory-bank/techContext.md index 46ec278..c3e06c1 100644 --- a/.memory-bank/techContext.md +++ b/.memory-bank/techContext.md @@ -42,9 +42,9 @@ source: repository and executable evidence ## Constraints -- Manifest: ModuleVersion 5.0.0, prerelease rc8, published 2026-10-20 (tag at - `c719197`). Latest stable 4.2.6. `$publishedVersions` ends at rc7; the reuse - test fails if it holds the manifest's version, so the stable change adds rc8. +- Manifest: ModuleVersion 5.0.0, prerelease rc8 (tag at `c719197`; `ai/rc9` + sets rc9). Latest stable 4.2.6. `$publishedVersions` ends at rc7; the reuse + test fails if it holds the manifest's version, so rc9 adds rc8, stable rc9. - Changed-section writes preserve unchanged owner/group/DACL/SACL (19). Roots use root-folder APIs, not AlphaFS device security (#41). - CHANGELOG contains user-visible changes only (7); tests and CI-only fixes