Browse Source

chore(memory-bank): record quality-gate evidence and release status

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: AI Assistant <ai@example.com>
pull/117/head
Raimund Andree 3 days ago
parent
commit
f11ff41294
  1. 103
      .memory-bank/activeContext.md
  2. 288
      .memory-bank/progress.md
  3. 148
      .memory-bank/systemPatterns.md
  4. 375
      .memory-bank/techContext.md

103
.memory-bank/activeContext.md

@ -1,6 +1,6 @@
---
status: current
last-verified: 2026-10-08
last-verified: 2026-10-09
owner: active-agent
source: current task evidence
---
@ -9,62 +9,57 @@ source: current task evidence
## Current focus
5.0.0-rc6 is on the PowerShell Gallery (tag `5.0.0-rc6` on `b51d970`, the
merge of #115); its GitHub release waits for a rerun of the failed Release
job. #116 (`ai/release-5.0.0-rc7`, base `master`) holds the behavior
changes that Phase 2 found, decided as assumptions for the maintainer's
review (Decision 22), and waits for that review. Then 5.0.0-rc7, Phase 3,
and 5.0.0; after 5.0.0 the repository is archived in favor of
WindowsAccessControl (Decision 18).
Quality-gate follow-up is implemented and validated locally on
`ai/quality-gate-coverage`, based on `d25647d` of #116. Code/test baseline
`3442194`, lab regression/acceptance `7594e0c`; final records follow.
No remote mutation. Architecture/cmdlet-design choices remain deferred;
Decision 22 is still proposed. Stable 5.0.0 is not ready (Decision 21).
## Evidence
- 2026-10-08, 5.0.0-rc6: the Release job of the tag (run `37839669028`)
published the package at 20:40 UTC and failed after it, because
`Publish-PSResource` gave up waiting after 100 seconds and its retry got
409 (`progress.md`, open work 8). The live tests of rc7 ran with
`-Version 5.0.0-rc6`, which checks the hash of the Gallery, in both
editions, 20:43 to 21:00 UTC: all passed except the warning text that
rc7 changed, which matches the live tests of rc6. The fixture was
removed at 21:03 UTC and its removal checked.
- 2026-10-08, #116, 11 commits on `be04cb7` (`3899228` to `1063b29`) and
commits of records:
- Decision 22: items 1, 2, 5, 6, 7, and 8 changed, 7 and 8 breaking (the
link cmdlets require `-Path` and `-Target` and write non-terminating
errors); items 3, 4, 9, and 10 kept, 9 with an FAQ entry. New defects,
fixed with a regression test that failed first: `Move-Item2` deleted
an empty folder that it moved to another volume (AlphaFS emulated the
move); the link cmdlets failed with `GetDefaultValueFailed` for every
piped object; `Get-NTFSSimpleAccess` failed for a folder that came
after its parent folder a second time.
- One `security-reviewer` pass over `be04cb7..4ee01e5`: no Blocker or
Major. Minor 1 to 5 and Nits 7 to 9 fixed test-first in `7936d9f` to
`1063b29`; Nit 7, the warning of `Get-NTFSEffectiveAccess` for names
of this computer, was reproduced first. Nit 6 declined (Decision 22).
- Suite of `1063b29`: 712 tests. Elevated: 688 passed and 24 skipped in
Windows PowerShell 5.1, 658 and 54 in PowerShell 7. As a basic user:
612 and 100, 582 and 130. No failure, none skipped in all four.
- Lab acceptance of `dc6e9f5` after the checkpoint
`ntfs-rc7-dc6e9f5-before-acceptance`, 16:24 to 16:40 UTC: 326 tests in
both editions, none failed, 2 skipped as in rc6
(`Tests/Lab/Acceptance-2026-10-08-5.0.0-rc7.md`). The code of
`4ee01e5` and a first run of `dc6e9f5` without the checkpoint had the
same counts. The fixture was removed at 16:21 and 16:44 UTC, and its
removal checked each time.
- #115 passed CI in all four configurations on `be04cb7`, with the first
runs of `Invoke-TestsAsBasicUser.ps1` on GitHub runners; #116 passed CI
on `ebe91fe` against the rc6 branch.
- #34: no reply from the tester since 2026-10-06.
- rc6 Release run `37839669028`, attempt 2, succeeded; GitHub prerelease
with zip appeared 2026-10-09 07:01:34 UTC. First attempt proves HTTP 409
after Gallery publication, not the previously assumed retry chronology.
- #116 is open, base master, head `d25647d`, CI build/wiki passed. rc7
publication is pending. The follow-up does not change that PR's head.
- Local changes: deletion/ownership guards (`a97e46f`); all scopes and
inheritance (`e7ee203`); absolute basic-user results (`51dec86`);
exact-package publication recovery (`95b827e`); first-hidden-item fix
(`d610372`); Force/descriptor guards (`3442194`); SMB regression above.
- Hidden omission was reproduced in all four configurations before the
fix. No parameter/design change. Publication tests are wholly mocked;
exact ordinal SHA-512 identity is required, no real upload occurred.
- Final uninstrumented suite: 914 each, zero failed. Passed/skipped:
elevated Desktop 890/24, Core 860/54; basic Desktop 749/165, Core 719/195.
Frozen aggregate: 2,641/3,559 sequence (74.21%), 974/1,933 branches
(50.39%); NTFSSecurity assembly 84.28%. All skipped templates have
executed counterparts; mutations restored exactly before green builds.
- Live packaged candidate, 09:20 to 09:51 UTC: 330 passed, zero failed,
two expected Server-module skips. Published rc6: 326 passed, four
expected failures (Hidden and rc7 warning text in each edition), two
skips. Tested folder and all 11 ZIP files are byte-identical.
- Temporary host result verifier failed on Desktop JSON wrapping/full
test names; corrected verification passed on unchanged raw results in
both editions. Cleanup wrapper's broad Error.Count was not acceptance
proof. Independent probes verified all fixture objects/members/profiles
gone from six machines. Raw failing markers and corrected evidence kept.
- One read-only independent code review approved, high confidence, no
significant findings or confirmed exploit. Custom reviewer could not
start (model unavailable); built-in code-review performed the one pass.
- Six checkpoints exist but report Standard, even after a successful
temporary ProductionOnly probe; policy restored, no restore performed.
Do not claim verified Production rollback evidence.
- Wider matrix not deployed: 13 Server 2025 VMs; Windows 11/2019/2022
media present, OS detection cache empty. #34 has no reply since Oct 6.
- Full evidence: session artifact `quality-gate-3442194-20261009`;
repository report `Tests/Lab/Acceptance-2026-10-09-quality-gate.md`.
## Next step
1. The maintainer reruns the failed Release job of 5.0.0-rc6, which skips
the published package and creates the GitHub release, and closes #110.
2. He pushes the records to #116 and reviews the choices of Decision 22,
each its own commit, above all the two breaking changes of the link
cmdlets. After the CI of the push, he merges #116 with a merge commit
(Decision 15) and tags `5.0.0-rc7`; the live tests then run against the
published package (`-Version 5.0.0-rc7`).
3. He decides the fix of the publish step (`progress.md`, open work 8) and
the scope of Phase 3: the operating systems, the code that nothing
calls, and file servers that aren't Windows (#34).
1. Maintainer pushes/reviews this follow-up; retain separate commits and
stacked-PR merge order (15). #116's Decision 22 review remains required.
2. Integrate and pass CI, then publish/test the next candidate package.
3. Close the remaining-path inventory (918 points, 562 for finer review),
decide/provision the OS matrix, obtain or explicitly accept #34 feedback.
4. Only then release 5.0.0 through documented CI steps; never claim the
current coverage percentage alone meets the quality gate.

288
.memory-bank/progress.md

@ -1,203 +1,121 @@
---
status: current
last-verified: 2026-10-08
last-verified: 2026-10-09
owner: active-agent
source: repository evidence
source: repository and validation evidence
---
# Progress
## Current status
5.0.0-rc6 is on the PowerShell Gallery, published by CI on 2026-10-08 at
20:40 UTC from the tag `5.0.0-rc6` on `master` (`b51d970`, the merge of
pull request #115; Decision 12). The Release job failed after the upload,
so the GitHub release waits for a rerun of the failed job. The published
package passed the live tests. Phase 2 of the quality gate (Decision 21)
is complete. The pull request #116 (`ai/release-5.0.0-rc7`) holds the
behavior changes that Phase 2 found, decided as assumptions for the
maintainer's review (Decision 22), and waits for that review. Phase 3
follows. The stable Gallery version is still 4.2.6. NTFSSecurity will be
archived soon; its users move to WindowsAccessControl (Decision 18).
5.0.0-rc6 is published on the Gallery and GitHub; its failed Release job
recovered in attempt 2 on 2026-10-09. #116 (rc7, `d25647d`, base `master`)
is open and green, not merged or published. Further quality-gate work is
local on `ai/quality-gate-coverage`; Phase 2 is not complete while the
remaining-path inventory is open. 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-04: #91 to #97 aligned the docs with the code,
shipped the help file, kept the docs on GitHub, set version 5.0.0, moved
CI and a wiki generated from `Docs` to GitHub Actions, and completed the
version history (Decisions 6 to 11).
- 2026-10-04: #98 (`e0f5366`) added releases on a version tag through CI
(Decision 12). The tag `5.0.0-rc1` published to the Gallery and created
the GitHub prerelease; the installed module passed the full suite.
- 2026-10-05, overnight run: the 24 code defects of work package 5 and the
issues #3, #4, #5, #17, #74, #82, #86, and #88 fixed with regression
tests, plus what one security review per PR found; the 37 open issues
triaged; `Docs/FAQ.md`, Dependabot for the actions, and the label `rc2`.
#5 and #82 are breaking changes, like the change of Decision 13.
- 2026-10-05: the first CI runs of the eight PRs found a defect that the
workstation had skipped, fixed in `629f4e7` (audit inheritance of an item
without a SACL). The PRs #99 to #106 were merged in order with merge
commits, CI on `master` passed, and the tag `5.0.0-rc2` published the
prerelease; GitHub's Actions outage that day cancelled the first two
attempts of the release run before they started. Repository hardening is
optional (Decision 14); the merge rule and the maintainer's rule for
fixes are Decisions 15 and 16.
- 2026-10-06: the issues got their labels (Decision 17). The maintainer
decided to publish 5.0.0-rc3 before 5.0.0 and to archive the project in
favor of WindowsAccessControl (Decision 18); the README, the docs home,
and the changelog announce it.
- 2026-10-06: 5.0.0-rc3 (#112): the access and audit cmdlets write only
the section that they change, which fixes #34 and the inherited entries
that elevated sessions copied as explicit ones (Decision 19). #67 has its
cause outside the module (a share root over UNC can't re-inherit), is
explained in `Docs/FAQ.md`, and was closed as not planned. One
`security-reviewer` pass approved the branch. The PR description said
"fixes #34", so the merge closed #34; it was reopened for a tester.
- 2026-10-06: 5.0.0-rc4 (#113), test-first: drive and volume roots read
and change their root folder, not the device (#41); the audit cmdlets
reject a descriptor without the audit entries (#109); `-WhatIf` previews
`Copy-Item2` and `Move-Item2` despite an existing destination (#108);
small items (#111). One `security-reviewer` pass approved it; its Minor
findings R1, R2, R6, and R8 were fixed before the merge. #41, #108,
#109, and #111 closed as completed, #90 and #107 as not planned.
- 2026-10-07: live tests in the lab of WindowsAccessControl (Decision 20)
against 5.0.0-rc2 and 5.0.0-rc4 in both editions: rc2 fails #34 over SMB
with error 1307 for `Add-NTFSAccess`, `Clear-NTFSAccess`, and
`Set-NTFSSecurityDescriptor`; rc4 passes the four cases, except
`Get-NTFSEffectiveAccess -ServerName` with a computer that can't be
reached, which returned no access since before rc1. The maintainer chose
to fix it test-first in 5.0.0-rc5; the branch build passes all live tests
and the suite. One `security-reviewer` pass approved it with minor
findings (`activeContext.md`).
- 2026-10-08: #114 merged (`fcb370e`); the tag `5.0.0-rc5` published it to
the Gallery and the GitHub releases, whose `NTFSSecurity.zip` holds the
same 11 files. Phase 1 of the quality gate (Decision 21) measured rc5:
the published package passes the live tests in both editions; the 11
tests that need a session without the Security privilege pass as a basic
user, so every test runs in at least one configuration, but CI runs only
elevated; the suite runs 55.9% of the C# lines and 37.4% of the branches.
The maintainer approved Phase 2.
- 2026-10-08: Phase 2, step 1 on `ai/release-5.0.0-rc6` (local): tests for
`Set-NTFSOwner`, `Test-Path2`, `Get-DiskSpace`, and the link cmdlets
(suite: 555 tests). Fixed test-first: the privileges stayed enabled after
an early stop; `Test-Path2` stopped for invalid characters in Windows
PowerShell; and, from one `security-reviewer` pass, the privilege cleanup
decided on stale states, a defect since 4.2.6. The page of
`New-NTFSSymbolicLink` was corrected after a lab check of Developer Mode.
- 2026-10-08: Phase 2 finished on `ai/release-5.0.0-rc6` (local). Tests for
`Get-NTFSOrphanedAudit`, `Get-NTFSSimpleAccess`, the
`-SecurityDescriptor` parameter sets, the error contracts of all path
cmdlets, and #110. Fixed test-first: `Get-NTFSSimpleAccess` (`ReadData`,
relative paths), `Copy-Item2` and `Move-Item2` (folder conflicts, the
missing destination folder of #21), the hard-link cmdlets on shares,
`Set-NTFSSecurityDescriptor -PassThru` (R5), and the error ID of
`Get-NTFSOrphanedAccess`; from the coverage report, relative paths that
start with a dot (every cmdlet acted on the item without the first two
characters), comparing output objects (`InvalidCastException`), and
`InheritedFrom`. CI runs the suite as a basic user too; the live tests
cover all cmdlet groups and accounts of three more domains. Suite: 677
tests, none failed, none skipped in every configuration; C# coverage
68.1% of the lines and 44.3% of the branches (rc5: 58.1% and 38.0%,
measured again; the first measurements counted one of four runs). Two
`security-reviewer` passes; the lab acceptance of `7b0781f` passed
(`Tests/Lab/Acceptance-2026-10-08-5.0.0-rc6.md`).
- 2026-10-08: #115 (rc6, head `be04cb7`) passed CI in all four
configurations. On `ai/release-5.0.0-rc7` (local), the behavior changes
of Phase 2 were decided as assumptions for review (Decision 22) and
implemented test-first, two of them breaking (the link cmdlets); new
defects found on the way: `Move-Item2` deleted an empty folder that it
moved to another volume, the link cmdlets failed for every piped object,
and `Get-NTFSEffectiveAccess` warned for names of this computer. One
`security-reviewer` pass (no Blocker or Major; its findings fixed but
one, declined). Suite and lab acceptance in `activeContext.md`.
- 2026-10-08: #115 merged (`b51d970`) and tagged `5.0.0-rc6`. The Release
job published the package at 20:40 UTC, then failed: `Publish-PSResource`
gave up waiting after 100 seconds while the Gallery accepted the upload,
and its retry got 409, so the job didn't create the GitHub release. The
published package passed the live tests of rc7 in both editions except
the one test whose expected warning text rc7 changed
(`Tests/Lab/Acceptance-2026-10-08-5.0.0-rc6.md`, After the release).
#116 (5.0.0-rc7) was opened on the rc6 branch and moved to `master`.
- 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`, through
`3442194`, adds 202 cases above rc7: deletion/owner failures, all 13
scopes, inheritance transitions, enumeration, forced replacement,
descriptor failures, and offline CI recovery. Reproduced/fixed rooted
result-path handling and first-hidden-item omission. Publication recovery
verifies exact SHA-512 identity, not merely version existence.
- 2026-10-09: final uninstrumented suite: 914 per configuration, zero
failures; coverage: 2,641/3,559 sequence points (74.21%) and 974/1,933
branches (50.39%), aggregate of four runs without AltCover `--save`.
All skipped templates have executed counterparts. Mutation guards were
proved and production source restored; Release build/checks pass.
- 2026-10-09: live comparison, 09:20 to 09:51 UTC: candidate 330 passed,
zero failed, two expected skips; published rc6 four expected Hidden/
warning-text failures only. Independent cleanup probes verified fixture
absence; raw host-verifier failures retained with corrected verification.
Lab guards/acceptance committed in `7594e0c`. One independent code review
approved with no significant finding (custom model unavailable; built-in
fallback). All 11 tested files match the ZIP. OS/path gates stay open.
## Stable capabilities
- 36 cmdlets: access (7), audit (5), inheritance (6), owner and security
descriptor (4), privileges (3), long-path items (6), links, hash, and
disk space (5).
- Works in Windows PowerShell 5.1 and PowerShell 7. In PowerShell 7,
`Get-FileHash2` lacks `RIPEMD160` and `MACTripleDES`, which .NET lacks.
- Pester tests in `Tests\` run in `$env:TEMP` sandboxes through
`Tests\TestHelpers.psm1`; tests that need privileges skip without them
and run in CI, whose runners are elevated.
- 36 cmdlets: access, audit, inheritance, owners/descriptors, privileges,
long-path items, links, hash, and disk space.
- Windows PowerShell 5.1 and PowerShell 7; RIPEMD160 and MACTripleDES are
available only in Desktop. Both editions run elevated/basic-user in CI.
- Pester fixtures use `Tests/TestHelpers.psm1` TEMP sandboxes. Live tests
are excluded from CI and run only on approved lab client/server targets.
## Open work
1. Quality gate before 5.0.0 (Decision 21): the maintainer reruns the
failed Release job of 5.0.0-rc6, which creates the GitHub release;
reviews the choices of Decision 22 in #116; merges #116 and tags
5.0.0-rc7, whose published package then runs the live tests. Phase 3
runs the live tests on more operating systems. Then release 5.0.0
through CI (Decision 12): remove the label, date
`[Unreleased]` as `[5.0.0]`, add the last prerelease to
`$publishedVersions`, and tag `5.0.0` (steps in
`Docs/Contributing/05-Releasing.md`). #34 stays open with Bug and Help
Wanted until a tester with a file server that refuses the owner
confirms the fix, or until 5.0.0 ships.
2. Issues: 5.0.0-rc6 addresses the seven items of #110 (tests); #115
named it without a closing keyword, so the maintainer closes it now.
#21 (a misleading error of `Move-Item2`) got
a fix in rc6 that names the missing destination folder; the folder
moves to another volume that rc7 fixes are a different defect. #68
tracks `-WhatIf` and `-Confirm` for every cmdlet that changes security.
The labels follow Decision 17; #16, #21, #45, and #89 wait for their
reporters (Needs Info). Not planned for 5.0.0: the enhancements #22,
#49, #68, #77, #87.
3. Review findings, not filed: of rc3, an extra DACL read and four SDDL
snapshots on read paths, and a duplicate SACL check; of rc4, R3 to R5,
R7, and five older defects, listed in the description of #113, among
them the accounts filter that `RemoveFileSystemAccessRuleAll` and
`RemoveFileSystemAuditRuleAll` ignore, which no cmdlet passes; of rc5,
the bare `catch` in `Win32.GetEffectiveAccess`, the unchecked
`AUTHZ_ACCESS_REPLY.Error`, and hardening of the lab controller (guards
in the setup blocks, interpolated `-EncodedCommand` paths, CredSSP by
IP address, the password string in memory, disabling the role accounts
after a run); of rc6, a privilege that fails to be disabled isn't tried
again by `Dispose` (finding 2, not reproducible); of rc7, one error ID
for a missing path in the audit cmdlets (declined, Decision 22).
4. Behavior changes found in Phase 2 (Decision 16): decided in Decision 22
as assumptions for the maintainer's review, on `ai/release-5.0.0-rc7`;
two of them are breaking changes of the link cmdlets. Left for Phase 3:
400 points of code that nothing calls besides the 244 lines of unused
classes.
5. `pwsh` 7.6.1 crashed three times during test runs on the ARM64
workstation (x64 emulation), without module frames; none of the CI runs
on native x64 on 2026-10-05 crashed.
6. Optional for the maintainer: delete the AppVeyor project and revoke its
GitHub authorization, restrict wiki editing to collaborators, ask
`Sup3rlativ3` to delete the Read the Docs project, and delete the branch
`test/transfer`. In the lab, delete the checkpoints
`ntfs-rc6-*-before-acceptance` and `ntfs-rc7-*-before-acceptance` of the
six machines when they are no longer needed.
7. Reachable code that no test runs (coverage report of rc6, ranked by
impact; about 300 points): `Remove-Item2` on folders (`-Recurse`,
`-Force`, `DeleteError`); the owner restore after taking ownership
(`RestoreOwnerError`); the inheritance cmdlets on folders and
`Set-NTFSInheritance -AccessInheritanceEnabled $true`; the mapping of
all 13 `-AppliesTo` values and the flag parameters of
`Remove-NTFSAccess`, `Add-NTFSAudit`, and `Remove-NTFSAudit`; the
switches and errors of `Get-ChildItem2`; the table views and
`InheritedFrom` in them; `Move-Item2 -Force`; account input errors;
`-PassThru` after success of the audit and inheritance cmdlets;
`Set-NTFSSecurityDescriptor` failures; the audit cmdlets without the
Security privilege on a local item; `Get-NTFSEffectiveAccess` for an
unresolvable SID; failed ownership retries of `Clear-NTFSAccess` and
`Set-NTFSInheritance`. A display limit, not a defect: a conditional ACE
shows as an unconditional entry, because the .NET rules have no
condition.
8. The publish step of the Release job fails when `Publish-PSResource`
gives up waiting after 100 seconds while the Gallery accepts the
package, because its retry gets 409 (5.0.0-rc6). Proposed for the
maintainer (Decision 16, not reproducible on demand; he was asked on
2026-10-08 and didn't answer, so it stays open): treat the error as
success when `Find-PSResource` then lists the version, in a script with
Pester tests. Until then, rerun the failed job.
1. Decision 21 gate: review Decision 22, integrate reviewed quality-gate
follow-up, publish the next candidate, and test the published package.
Do not release 5.0.0 until the remaining-path and OS-matrix gates close.
Release steps: `Docs/Contributing/05-Releasing.md`; remove prerelease
label, date `[5.0.0]`, update `$publishedVersions`, 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
ShouldProcess for security cmdlets; enhancements #22/#49/#68/#77/#87
are not planned for 5.0.0. Labels follow Decision 17.
3. Deferred reviews (not silently accepted): rc3 extra DACL read/SDDL
snapshots/duplicate SACL check; rc4 findings listed in #113, including
library-only RemoveAll account filters; rc5 unchecked Authz errors and
lab-controller hardening; rc6 failed privilege-disable retry (not
reproduced); rc7 audit missing-path error IDs declined in Decision 22.
4. Architecture/cmdlet design: Decision 22 remains proposed; two link
changes are breaking. Keep unused classes/helper overloads until a
maintainer decision; do not remove them to improve coverage percentages.
5. ARM64 workstation: PowerShell 7.6.1 crashed under x64 emulation without
module frames; native-x64 CI did not reproduce it.
6. Optional maintainer cleanup: obsolete AppVeyor/Read the Docs access,
wiki editing restrictions, `test/transfer`, and old lab checkpoints
when no longer needed. No remote changes or snapshot restores here.
7. Fresh coverage inventory at `3442194`: 918 unvisited sequence points.
Of these, 244 are in classes unused by cmdlets and 112 in parameter
getters; 562 remain for finer review/testing, including unused overloads,
defensive/native failures, and environment-specific branches. High-value
local gaps closed: folders/Force/DeleteError, RestoreOwnerError, all
scopes, file/folder inheritance, enumeration/depth/link skipping,
descriptor write failures, and forced file replacement. Remaining
candidates: audit ownership-retry failures, SD inheritance edge cases,
effective-access unresolved identity, recursive denial/error surfaces,
output-object comparisons/formatting. A conditional ACE display remains
a .NET representation limit, not evidence of unconditional permissions.
8. Publication recovery is implemented locally in `95b827e`, with 14 offline
tests and exact artifact SHA-512 verification. Original upload errors
remain errors for missing/different/unverifiable outcomes. Not deployed
until the maintainer merges/pushes; no publication was performed here.
9. Phase 3: choose OS scope (proposed Windows 11 client/2019/2022 servers),
detect ISO editions, provision without repurposing shared VMs, then run
published-package acceptance. #34 has no new reply since 2026-10-06.
10. Lab rollback evidence: new checkpoints exist but report Standard even
after a successful temporary ProductionOnly probe. Classification is
unresolved; original VM policy restored, no checkpoint restored. Do
not represent these as verified Production snapshots.

148
.memory-bank/systemPatterns.md

@ -1,57 +1,27 @@
---
status: current
last-verified: 2026-10-08
last-verified: 2026-10-09
owner: active-agent
source: repository evidence
source: repository and regression evidence
---
# System patterns
## Architecture
```text
NTFSSecurity.psd1 ─┬─ ScriptsToProcess: NTFSSecurity.Init.ps1
│ Add-Type: Security2.dll, PrivilegeControl.dll,
│ ProcessPrivileges.dll, inline NTFS.DriveInfoExt;
│ Update-FormatData -PrependPath format.ps1xml
├─ TypesToProcess: NTFSSecurity.types.ps1xml
│ (Owner, IsInheritanceBlocked, LengthOnDisk on
│ FileInfo/DirectoryInfo; AccountType on ACEs)
├─ RootModule: NTFSSecurity.psm1 (aliases)
├─ NestedModules: NTFSSecurity.dll (36 cmdlets)
└─ en-US\NTFSSecurity.dll-Help.xml (Get-Help; generated
from Docs/Cmdlets, Decision 8)
NTFSSecurity.dll ── cmdlets ──> Security2.dll (FileSystemAccessRule2,
FileSystemAuditRule2, IdentityReference2,
FileSystemInheritanceInfo, EffectiveAccess)
── long paths ──> AlphaFS
── privileges ──> PrivilegeControl / ProcessPrivileges
```
- `BaseCmdlet` resolves only relative paths, against the current file
system location of the session (not `$PWD`, #86). Path parameters carry
`[FileSystemPathTransformation]`, which binds file objects as full paths.
- On access denied, most cmdlets retry through `InvokeAsOwner`, which takes
ownership and restores the previous owner on every exit path.
- `BaseCmdletWithPrivControl` enables Backup, Restore, TakeOwnership, and
Security in `BeginProcessing` when `PrivateData.EnablePrivileges` is
`$true`, and disables the ones it enabled in `EndProcessing` and, since
5.0.0-rc6, in `Dispose`: PowerShell skips `EndProcessing` when a later
command, such as `Select-Object -First`, or a terminating error stops the
pipeline, but calls `Dispose`. `Enable-Privileges` keeps them
(`KeepEnabledPrivileges`). The cleanup reads the current state of each
privilege, because another command in the pipeline can have changed it,
and tries every privilege even when one fails: `EndProcessing` warns,
`Dispose` stays silent, because PowerShell ignores exceptions thrown
there and no stream is open anymore.
- `PrivateData` switches: `EnablePrivileges`, `GetInheritedFrom`,
`GetFileSystemModeProperty`, `IdentifyHardLinks`, `ShowAccountSid`.
- Cmdlets accept `-Path` (alias `FullName`) or `-SecurityDescriptor`; the
SD sets change the object in memory until `Set-NTFSSecurityDescriptor`.
| Component | Responsibility |
| --- | --- |
| `NTFSSecurity.psd1` | Root script, nested binary, initialization, types, help |
| `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 |
| `en-US/NTFSSecurity.dll-Help.xml` | Committed help generated from cmdlet Markdown |
## Decisions
Each Decision record is a file in `decisions/`; read only the relevant ones.
Read only task-relevant records; the index controls routing.
| # | Decision |
| --- | --- |
@ -80,65 +50,45 @@ Each Decision record is a file in `decisions/`; read only the relevant ones.
## Patterns
### Writing cmdlets
### Cmdlets and security sections
- A parameter that takes pipeline input needs a getter that doesn't
throw: PowerShell reads it before it binds each input object, and an
exception turns every object into `GetDefaultValueFailed` (the link
cmdlets before 5.0.0-rc7).
- An error for one item is non-terminating, so that the cmdlet goes on
with the next path or pipeline object; since 5.0.0-rc7, the link cmdlets
too. Its message names the item, and its target object is the item that
the cmdlet was asked to process. Resolving a path can throw in Windows
PowerShell for an invalid character, so that belongs inside the
per-item error handling.
- Folders move without `MoveOptions.CopyAllowed`: for another volume,
AlphaFS then copies and deletes, which lost empty folders. Windows
refuses such a move with `NotSameDeviceException` (17). Tests reach
another volume through `\\localhost\C$`, elevated only.
- BaseCmdlet resolves relative paths against the current filesystem
location; file-object input binds FullName through path transformation.
- Write only changed/read sections (Decision 19); a descriptor parameter
changes memory until `Set-NTFSSecurityDescriptor` persists it.
- Access denial can retry through InvokeAsOwner; restore the previous owner
on every exit, except a successful descriptor write that intentionally
sets the owner. Restoration failures must report RestoreOwnerError.
- Privilege cleanup runs in EndProcessing and Dispose, reads current states,
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.
### Verifying documentation
### Tests and documentation
- Run platyPS in Windows PowerShell 5.1 against a Release build; a copy of
`Docs/Cmdlets` must round-trip through `Update-MarkdownHelp` unchanged.
Keep cmdlet pages ASCII-only. platyPS takes `Position` and `Required` from
the shipped help file: after such a change, edit the page YAML, run
`New-ExternalHelp`, rebuild, and check the round trip.
- MarkdownLinkCheck checks relative `Docs` links, `Tests\Wiki.Tests.ps1`
the wiki links and anchors. The wiki is generated from `Docs` (never edit
it); `Docs/README.md` becomes Home, its cmdlet groups the sidebar.
- In cmdlet pages, end a sentence with a link (platyPS drops the space
after it). Verify examples in a `$env:TEMP` sandbox, never on real data.
- Tests import Release in isolated processes, both editions and privilege
modes. File/ACL/link fixtures use shared sandbox guards and cleanup.
Privilege-dependent skips must have eligible counterparts in the matrix.
- Assert persisted state, errors/targets, continuation, and no failed
PassThru output. Prove new characterization guards with bounded mutations;
restore source exactly and rebuild before green validation or packaging.
- 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.
- Live tests use only approved lab targets, SMB then independent server state;
Get/SetFileSecurity preserves stored DACLs; rights oracles use S4U tokens.
### Testing the module
### CI results and publication
- Pester 5 tests in `Tests/*.Tests.ps1` import the Release build; CI runs
them in Windows PowerShell 5.1 and PowerShell 7 (Decision 11).
- A test that changes files, links, or security descriptors uses
`Tests\TestHelpers.psm1`: its own sandbox, `Assert-TestSandboxPath`
before each change, `Remove-TestSandbox`. Cases that need a privilege
skip with `Test-PrivilegeHeld`, cases that need its absence skip when
elevated; CI runs the suite elevated and as a basic user in both
editions, so each case runs somewhere. `Block-Test*` make a read or a
write fail without elevation; `Set-TestOwner` with
`EnablePrivileges = $false` reproduces an owner the user can't assign.
- Fixtures write a DACL with `SetAccessControl`, never with `Set-Acl`:
`Set-Acl` compares `AreAuditRulesProtected` of the new descriptor with
`AreAccessRulesProtected` of the item (`FileSystemSecurity.cs` of
PowerShell), so for an item with a protected DACL it writes the audit
section too. Without the Security privilege that fails with
`PrivilegeNotHeldException`; with it, `Set-Acl` writes every section and
drops the audit entries. Windows PowerShell has
`FileInfo`/`DirectoryInfo.SetAccessControl`; PowerShell 7 has
`[System.IO.FileSystemAclExtensions]::SetAccessControl`.
- `Get-Help -Online` tests run only in Windows PowerShell, which honors the
hook `BypassOnlineHelpRetrieval`. `Manifest.Tests.ps1` and
`Release.Tests.ps1` check the manifest, the version (Decision 10), the
release notes, and the packages.
- The live tests in `Tests\Lab` (Decision 20) run as domain accounts in a
lab: on the client over SMB, then on the file server, which checks what
the client runs left. They read and write descriptors as Windows stores
them with `GetFileSecurity` and `SetFileSecurity`, because
`GetNamedSecurityInfo` converts a DACL without the auto-inherit flag and
returns its owner. The expected effective rights come from the S4U tokens
of the file server and the client, like the Effective Access tab.
- Use Path.Combine then GetFullPath for a rooted-or-repository-relative
result path; Join-Path appends even a rooted child and corrupts it.
- Discovery handles only expected PackageNotFound as absence; repository,
authentication, and network errors remain failures.
- Rerun/uncertain-upload success requires Gallery SHA-512 equality with the
exact build artifact. Base64 is case-sensitive: use ordinal comparison.
Missing/different/unverifiable metadata preserves the upload error.
- Secrets stay by environment reference, never in process arguments/logs.
Test all external publication commands with mocks; no test may upload.
- AltCover aggregates all four sequential runs without --save. Report
sequence points, not unique source lines; keep unmatched paths visible.

375
.memory-bank/techContext.md

@ -1,251 +1,156 @@
---
status: current
last-verified: 2026-10-08
last-verified: 2026-10-09
owner: active-agent
source: repository evidence
source: repository and executable evidence
---
# Tech context
## Stack
- C# class libraries, old-style `.csproj`, .NET Framework 4.5.2,
solution `NTFSSecurity.sln` (Visual Studio 2017 format).
- Projects: `NTFSSecurity` (cmdlets), `Security2` (ACL object model, Win32
interop), `PrivilegeControl` and `ProcessPrivileges` (token privileges),
`Log`, `TestClient`, `NTFSSecurityTest` (MSTest, minimal coverage).
- NuGet (`packages.config`): AlphaFS 2.2.x for long paths;
`System.Management.Automation.dll` 10.0.10586.0. For a drive or volume
root, AlphaFS `DirectoryInfo` reaches the device object, while
`Directory.Get/SetAccessControl('C:\')` reaches the root folder (#41).
- Module: `NTFSSecurity.psd1` loads `NTFSSecurity.psm1` (aliases `dir2`,
`gi2`, `rm2`, `del2`), `NTFSSecurity.Init.ps1` (Add-Type of the helper
assemblies, prepends `NTFSSecurity.format.ps1xml`), and `NTFSSecurity.dll`.
- Documentation: Markdown in `Docs` and `README.md`, rendered by GitHub and
published to the wiki by CI; no documentation site (Decisions 9 and 11).
Cmdlet pages are platyPS 0.14 markdown (schema 2.0.0) in `Docs/Cmdlets`.
- Help: `NTFSSecurity\en-US\NTFSSecurity.dll-Help.xml`, generated from
`Docs/Cmdlets` and committed (Decision 8).
- Tests: Pester 5 in `Tests`, one file per area, against the Release
build; `Wiki.Tests.ps1` (wiki conversion) runs without a build.
- CI: GitHub Actions, `.github/workflows/ci.yml` with the scripts in
`.github/scripts` (Decision 11).
- Legacy C# projects, .NET Framework 4.5.2, `NTFSSecurity.sln`.
Cmdlets depend on Security2, PrivilegeControl/ProcessPrivileges, and
AlphaFS 2.2.x. System.Management.Automation reference: 10.0.10586.0.
- Module supports Windows PowerShell 5.1 and PowerShell 7; 36 cmdlets.
Manifest initializes helper assemblies, aliases, type data, formatting,
and committed help generated from `Docs/Cmdlets` (platyPS 0.14/schema 2).
- CI: `.github/workflows/ci.yml`, scripts in `.github/scripts`; GitHub
renders Docs and publishes a generated wiki. No separate docs site.
## Environment
## Current environment
- Windows only (NTFS, Win32 security APIs).
- The Debug build writes straight into
`C:\Program Files\WindowsPowerShell\Modules\NTFSSecurity\`.
- No Visual Studio MSBuild or .NET Framework targeting pack on the
workstation. A local build works with the .NET Framework MSBuild
(`%WINDIR%\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe`) plus
`/p:CscToolPath` to the Roslyn `csc.exe` of the `Microsoft.Net.Compilers`
package; the legacy C# 5 compiler fails with CS0136. `dotnet msbuild`
fails on the binary resources in `Resources.resx` (MSB3822, MSB3823).
- platyPS 0.14.2, Pester 5.7.1, PSScriptAnalyzer, and powershell-yaml are
installed only for PowerShell 7. Windows PowerShell 5.1, started from
PowerShell 7, imports platyPS and Pester by full path
(`~\OneDrive\Documents\PowerShell\Modules\platyPS\0.14.2`,
`C:\Program Files\PowerShell\Modules\Pester\5.7.1`). Leave
`$env:PSModulePath` alone: PowerShell 7 hands the child the Windows
PowerShell default path, and clearing it leaves Windows PowerShell without
its core modules (Pester fails: `Add-Member` not found).
- MarkdownLinkCheck is not installed, and `Save-Module` crashed (FailFast)
in PowerShell 7.6 on 2026-10-04. Download the 0.2.0 package from
`https://www.powershellgallery.com/api/v2/package/MarkdownLinkCheck/0.2.0`
into `$env:TEMP`, extract it, and import it by path.
- The first workstation is ARM64; PowerShell 7 runs as x64 under emulation.
- The NuGet cache (`~\.nuget\packages`) holds every build dependency: copy
`alphafs\2.2.1`, `system.management.automation.dll\10.0.10586`, and
`microsoft.netframework.referenceassemblies.net452\1.0.3` into
`packages\<Id>.<Version>`, and point `CscToolPath` at
`microsoft.net.compilers\4.2.0\tools`.
- The second workstation (x64, used since 2026-10-05) runs the agent
session elevated, so the tests that need privileges run there as in CI.
It has no NuGet cache with these packages: download each from
`https://api.nuget.org/v3-flatcontainer/<id>/<version>/<id>.<version>.nupkg`,
extract the first three into `packages\<Id>.<Version>` and the compilers
into `$env:TEMP`; Pester 5.7.1 comes from the Gallery package API the
same way, its folder first on `$env:PSModulePath` of the test process.
The GitHub CLI is in `C:\Program Files\GitHub CLI`, outside the PATH of
sessions started before its installation.
- The third workstation (`ExHost`, a Windows Server 2025 VM, x64, used since
2026-10-07) runs the agent session elevated and hosts the AutomatedLab lab
`WindowsAccessControlLab` (Decision 20) with Hyper-V and AutomatedLab
5.61.704. It has no NuGet cache or platyPS: check each
nuget.org package against the SHA-512 `packageHash` of its catalog entry
(`https://api.nuget.org/v3/registration5-semver1/<id>/<version>.json`,
then `catalogEntry`), and each Gallery package against `PackageHash` of
`api/v2/Packages(Id='<id>',Version='<version>')`. Pester 5.7.1 is in
`V:\Git\WindowsAccessControl\output\RequiredModules`. The GitHub CLI
2.102.0 is in `C:\Program Files\GitHub CLI`, outside the PATH, and signed
in as `raandree` since 2026-10-08; `Block-RemoteMutation` denies its
mutating commands, so the agent uses it read-only. The lab domains
`a.forest1.net` and `b.forest1.net` had a maximum password age of 42
days, so the password of `install` expired on 2026-09-15 and AutomatedLab
got access denied; it never expires since 2026-10-07, as in
`forest1.net`.
- Host `ExHost`: Windows Server 2025 VM, native x64, elevated agent;
repository `V:\Git\NTFSSecurity`. AutomatedLab 5.61.704, Hyper-V,
approved lab `WindowsAccessControlLab` (Decision 20).
- Build Release only: Debug writes to Program Files. Native .NET Framework
MSBuild plus Roslyn `Microsoft.Net.Compilers` 4.2.0 and .NET 4.5.2
reference assemblies work; legacy compiler fails CS0136, dotnet MSBuild
fails binary resources MSB3822/MSB3823. Build packages are already cached
in `packages`; compiler/tools are under TEMP `ntfs-build`.
- Pester 5.7.1 is in
`V:\Git\WindowsAccessControl\output\RequiredModules\Pester\5.7.1`.
platyPS 0.14.2 and MarkdownLinkCheck 0.2.0 are under TEMP `ntfs-docs-tools`.
PSScriptAnalyzer and PSResourceGet are available in PowerShell 7.
- Use Desktop's module paths in Desktop children, not inherited Core-only
paths. Never import NTFSSecurity in the agent shell; every package/build
runs in a new process. Current prereleases share assembly version 5.0.0.0.
- GitHub CLI: `C:\Program Files\GitHub CLI\gh.exe`, signed in as raandree.
Read-only queries work; remote mutations belong to the maintainer.
- LabSources: `V:\LabSources`. All 13 deployed machines are Server 2025.
Windows 11 consumer/enterprise-evaluation media and Server 2019/2022
ISO files exist. OS cache is empty; exact detected editions are not yet
verified. Do not equate present media with a deployed/tested OS matrix.
## Constraints
- `ModuleVersion` is `5.0.0` with the prerelease label `rc6` on the branch
`ai/release-5.0.0-rc6` (`rc5` on `master`).
The latest stable tag and Gallery release is `4.2.6`. The manifest
requires PowerShell 5.1 and .NET Framework 4.5.2, uses `RootModule`, and
lists exactly 36 cmdlets; `Test-ModuleManifest` passes in Windows
PowerShell 5.1 and PowerShell 7.6.
- The module source at `master` differs from tag `4.2.6` by the changes
that `CHANGELOG.md` lists under `[Unreleased]`, the release notes of each
5.0.0 prerelease.
- PowerShell Gallery versions (publish dates): 4.0.0 (2015-08-19), 4.2.2
(2016-05-18), 4.2.3 (2016-05-19), 4.2.4 (2018-08-13), 4.2.5 (2019-07-11),
4.2.6 (2019-07-12), none with release notes; 5.0.0-rc1 (2026-10-04),
5.0.0-rc2 (2026-10-05), 5.0.0-rc3 and 5.0.0-rc4 (2026-10-06), 5.0.0-rc5
(2026-10-08), published
by CI. Older versions were released on CodePlex only, and their dates are
lost. The git history starts on 2016-10-10, when the project moved from
CodePlex.
- Releases up to 4.2.6 were Debug builds published by hand, with the whole
output folder; their tags carry the previous version. From 5.0.0 on, CI
publishes on a version tag (Decision 12). GitHub releases attach
`NTFSSecurity.zip`.
- CI: GitHub Actions on pull requests, pushes to `master`, and version tags
(Decision 11); AppVeyor and Read the Docs aren't used (Decision 9).
- `CHANGELOG.md` lists user-visible changes only; CI and build-only changes
get no entry
([Decision 7](decisions/0007-changelog-user-visible-only.md)).
- Remote mutations are the maintainer's: the user-level preToolUse hook
`Block-RemoteMutation.ps1` denies `git push` and mutating `gh` commands
(`pr create`, `pr close`, and others) from the agent session, even after
an explicit request. Its override, `COPILOT_ATELIER_ALLOW_REMOTE=1`, is
read from the environment that VS Code starts the hook with; setting it
inside an agent command has no effect (verified 2026-10-04). The hook
matches the whole command text, so a commit message that quotes such a
command is blocked too. Prepare the commands and descriptions; the
maintainer runs them. Hand over each command as its own fenced code block
at the end of the reply, which the chat shows with a copy button, and end
the turn there; the maintainer reports back in the chat. The question
dialog joins the lines of its text, has no copy button, and covers the
reply before it (maintainer, 2026-10-06). A long question also hides its
choices, so that it can't be answered: keep it to a few short sentences
(2026-10-07). A pull request description
names an issue without a closing keyword (fixes, closes, resolves) unless
the merge should close it: "fixes #34" in #112 closed #34. Simulated `gh`
commands in offline tests must print what the real ones print, such as
the URL of a new comment.
- Source manifest: ModuleVersion 5.0.0, prerelease rc7 on #116/follow-up.
Latest stable: 4.2.6; latest published prerelease: rc6 (2026-10-08).
GitHub rc6 release recovered 2026-10-09. rc7 publication is pending.
- 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
get no entry. No stable release until Decision 21 gates close.
- Honor separate topic branches, no amendment, two AI co-author trailers.
Never work around remote-mutation blocking. Provide each maintainer
command separately at reply end, no question dialog after commands.
Issue references use no closing keyword unless closure is intended.
- Lab passwords stay in memory and are lab-only; no secret in repository,
logs, or process arguments. Live ACL mutations occur only in the lab.
- Existing expired installation passwords of a.forest1/b.forest1 were
configured not to expire on 2026-10-07, matching the root domain.
## Validation
## Build and focused checks
- CI (`.github/workflows/ci.yml`): job `build` on `windows-2025` installs
platyPS 0.14.2, MarkdownLinkCheck 0.2.0, and Pester 5.7.1 for all users,
restores `packages.config` per project plus
`Microsoft.NETFramework.ReferenceAssemblies.net452` 1.0.3, builds
`NTFSSecurity.csproj` in Release with the MSBuild that `vswhere` finds,
then: 01 `Update-MarkdownHelp` and fail on `git diff -- Docs/Cmdlets`; 02
`Get-MarkdownLink -BrokenOnly`; 03 regenerate the help file and fail on
`git status --porcelain -- NTFSSecurity/en-US`; 04 `Invoke-Tests.ps1` in
Windows PowerShell 5.1 and in PowerShell 7, then
`Invoke-TestsAsBasicUser.ps1` in both editions (since 5.0.0-rc6). Job
`wiki` on `ubuntu-latest`
(read-only) clones the wiki (`gh auth setup-git` with the built-in token),
runs `Export-WikiContent.ps1`, and lists the changed pages in the job
summary; job `publish-wiki` (`contents: write`) repeats that and publishes,
for `master` only. After the tests, `build` runs
`New-ModulePackage.ps1` and uploads the artifact `packages` (nupkg and
`NTFSSecurity.zip`). Job `release` runs only for tags matching
`[0-9]+.[0-9]+.[0-9]+` or `[0-9]+.[0-9]+.[0-9]+-*`, in the environment
`powershell-gallery` (secret `PSGALLERY_API_KEY`); see Decision 12.
Actions are pinned by commit SHA: `actions/checkout` v7.0.1,
`actions/upload-artifact` v7.0.1, `actions/download-artifact` v8.0.1;
Dependabot proposes updates weekly, one week after a release.
- Packaging needs PSResourceGet (`Compress-PSResource`, PowerShell 7.4 or
later); its tests skip in Windows PowerShell. Dry run locally: run
`New-ModulePackage.ps1` against `NTFSSecurity\bin\Release` into
`$env:TEMP`, then extract the nupkg into a folder and import it there.
- Read CI runs with `gh run list --repo raandree/NTFSSecurity --workflow
ci.yml`, `gh pr checks <number>`, and `gh run view <id> --log-failed`
(read-only).
- Workflow lint: actionlint (download the release zip into `$env:TEMP` and
check its SHA-256 against the checksum file; 1.7.12 on 2026-10-08);
PowerShell steps check
`$LASTEXITCODE` after every native command, because GitHub checks only
the last one.
- Run platyPS in Windows PowerShell 5.1 to avoid PowerShell 7.4+
`-ProgressAction` noise.
- Placeholder check: no `{{` left in `Docs/Cmdlets/*.md`.
- Help file: `New-ExternalHelp -Path .\Docs\Cmdlets -OutputPath
.\NTFSSecurity\en-US -Force` must leave `git status` unchanged.
- Pester: run detached (`Start-DetachedPowerShell.ps1`) in Windows
PowerShell 5.1: the launcher starts `pwsh`, and its payload runs
`powershell.exe -NoProfile -EncodedCommand` with Pester imported by full
path. A run without `bin\Release\en-US` must fail.
- Tests that run only without a privilege skip in an elevated session.
`.github\scripts\Invoke-TestsAsBasicUser.ps1` runs the suite from an
elevated session with a token of the SAFER level Normal User, like
`runas /trustlevel:0x20000`, and CI runs it in both editions. For a
single file, `runas /trustlevel:0x20000` works too; give Windows
PowerShell its own `PSModulePath`, and note that `runas` returns at once,
so the script it starts writes its own log. Both tokens hold only the
privilege to bypass traverse checking.
Pester reports a skipped `-ForEach` test under its template name, such as
`<_> should ...`, and a test that ran under the expanded name: compare
runs by template.
- C# coverage (Decision 21): AltCover 9.0.145 (`tools\net472\AltCover.exe`
of the nuget.org package) instruments a copy of the local Release build,
which has the PDB files that the published package lacks:
`--reportFormat=OpenCover`, AlphaFS and `System.Management.Automation`
excluded with `--assemblyFilter`, and no `--save`: then every process
writes its hits into the report when it exits. With `--save`, each
process writes a recorder file, and `runner --collect` keeps only the
first one (verified 2026-10-08), so the numbers measured that way held
only the main process of the elevated Windows PowerShell run. Put the
instrumented module in `NTFSSecurity\bin\Release` of a `git worktree`,
run `.github\scripts\Invoke-Tests.ps1` elevated and
`Invoke-TestsAsBasicUser.ps1` in both editions, then
`AltCover.exe runner --collect --recorderDirectory=<the instrumented
folder>`, which recalculates the summary of the report from the hits.
All four configurations, 2026-10-08: the rc5 tree 58.1% of the lines
(2,020 of 3,476) and 38.0% of the branches (711 of 1,873), 62.5% without
244 lines in classes that no cmdlet calls; the rc6 candidate (`1b9edbb`)
68.1% of the lines (2,412 of 3,540) and 44.3% of the branches (850 of
1,918), 73.2% without those classes, the `NTFSSecurity` assembly 78.1%.
The earlier figures, 55.9% for rc5 and 65.6% for rc6, used `--save`.
- Live tests (Decision 20): in an elevated Windows PowerShell 5.1 session
on the lab host, `Tests\Lab\Invoke-NTFSSecurityLabTest.ps1` with
`-Version` for Gallery packages or `-ModulePath` for a build; it writes
the results to `$env:TEMP\NTFSSecurityLab\Results`. A run of two versions
in both editions takes about 30 minutes; `-RemoveFixture` removes its
accounts, share, and folders from the lab. For a check on the client as
an account without administrator rights, use `NtfsLiveServerAdmin`
(Remote Management Users on the client, CredSSP by IP address like the
controller): reset its password on the PDC emulator to a random value
in memory; the next run of the controller sets a new one anyway.
- Lab acceptance of a candidate (modeled on the WindowsAccessControl
handoff 07): build once, package it with `New-ModulePackage.ps1`, and
record the SHA-256 of the packages and module files; check WinRM, LDAP
(RootDSE), Kerberos (`klist get`), the secure channel, and the clock of
the six VMs; take a Production checkpoint named
`ntfs-<label>-<commit>-before-acceptance` of `F1ADC1`, `F1BDC1`,
`F2DC1`, `F3DC1`, `F1AFile1`, and `F1AFile2`; run the controller with
`-ModulePath` of the extracted `NTFSSecurity.zip` in both editions; then
`-RemoveFixture` and check that the accounts, share, folders, group
memberships, and profiles are gone.
- `Get-NTFSEffectiveAccess -ServerName`: the authorization manager of the
named computer answers only its administrators and the members of its
group Access Control Assistance Operators (S-1-5-32-579); others get
"Access is denied" (5). Lab probe of 2026-10-08 on `F1AFile2`.
- Markdown lint: `npx markdownlint-cli2` with `MD013` limited to prose
(tables, code, and headings excluded) on the conceptual pages; for
`CHANGELOG.md` also `MD024` with `siblings_only: true`, because every
version repeats the category headings.
- Gallery packages: download
`https://www.powershellgallery.com/api/v2/package/NTFSSecurity/<version>`
into `$env:TEMP` and extract it; dates come from the OData endpoint
`api/v2/FindPackagesById()?id='NTFSSecurity'`. Import each version in its
own process: every version's `NTFSSecurity.dll` has assembly version
4.2.1.0, so a second version in the same process reuses the first DLL.
- YAML: `ConvertFrom-Yaml` (powershell-yaml) on `.github/workflows/ci.yml`.
- Links: the CI step 02 (MarkdownLinkCheck 0.2.0) checks only relative
links in `Docs`; it strips anchors and skips absolute URLs.
`Wiki.Tests.ps1` checks the wiki links with their anchors; check the
links in `README.md` and `CHANGELOG.md` with a script.
- Build `NTFSSecurity\NTFSSecurity.csproj` with Configuration=Release,
Framework MSBuild, TargetFrameworkRootPath/FrameworkPathOverride to
`packages\Microsoft.NETFramework.ReferenceAssemblies.net452.1.0.3\build`,
CscToolPath to the cached compiler. Expected legacy CS1591/CS0618 warnings
are not new failures. Never copy a mutated DLL into acceptance artifacts.
- Pester/builds run in detached monitored child processes through
`Start-DetachedPowerShell.ps1`; use unique TEMP logs/result paths and an
explicit-PID watcher. No foreground sleep/poll loop. Long payloads use
a script file: nested Base64 encoding can exceed Windows command limits.
- Focused helper: TEMP `ntfs-focused\Start-FocusedRuns.ps1`; detach that
driver too because its internal wait loop must not block the agent shell.
- TEMP `ntfs-docs-tools\Invoke-ChangeChecks.ps1 -File <relative paths>`
performs AST/analyzer/lint/help checks. Absolute input paths misroute
cmdlet pages. Check actual analyzer/lint output, not just helper exit.
- actionlint 1.7.12 checks the workflow. Script changes use AST parse and
PSScriptAnalyzer; prose Markdown uses MD013 and changelog siblings-only
repeated-heading allowance. Native error codes must be checked explicitly.
- Documentation: run platyPS in Desktop, generate external help, rebuild,
require an unchanged Markdown round trip. Links in Docs are checked
relatively; Wiki tests cover generated anchors, not arbitrary web URLs.
## CI and packaging
- CI `build` on windows-2025 installs tools, restores dependencies, builds
Release, round-trips pages/help, checks links, and runs the suite in
Desktop/Core, elevated/basic user. Lab tests are explicitly excluded.
- `.github/scripts/Invoke-TestsAsBasicUser.ps1` launches a SAFER Normal User
token. Result paths may be absolute or repository-relative: Path.Combine
then GetFullPath, not Join-Path with a rooted child.
- Packaging needs Compress-PSResource (Core 7.4+). New-ModulePackage copies
only FileList, validates the manifest, creates nupkg plus NTFSSecurity.zip.
Check package/file hashes and test the extracted artifact, not build extras.
- Release runs only for validated version tags in powershell-gallery.
API key stays as PSGALLERY_API_KEY environment reference. Helper
Publish-ModulePackage treats only PackageNotFound as expected absence;
existing-version skip and uncertain-upload recovery require exact Gallery
SHA-512 equality. Base64 comparison is case-sensitive. Unverifiable,
missing, and different outcomes preserve errors. No test uploads.
- Read status through gh pr checks / gh run view --log-failed. A successful
Gallery upload followed by HTTP 409 does not prove its retry chronology.
## Coverage and test eligibility
- AltCover 9.0.145 net472 instruments a copied Release build with PDBs,
OpenCover format, localSource, excluding AlphaFS/System.Management.Automation.
Do not use --save: collection previously retained only one process's hits.
- Freeze a git worktree, instrument its NTFSSecurity\bin\Release, run all
four configurations sequentially with the real CI wrappers, then
AltCover runner --collect recalculates the report. Compute option paths
before passing native arguments, not inline Join-Path expressions.
- Report sequence points, not unique source lines. Four-run baselines:
rc5 2,020/3,476 (58.1%), branches 711/1,873 (38.0%);
rc6 2,412/3,540 (68.14%), branches 850/1,918 (44.32%);
follow-up `3442194` 2,641/3,559 (74.21%), 974/1,933 (50.39%).
NTFSSecurity assembly: 1,769/2,099 (84.28%). Different code changes
denominators; never present these as same-source incremental percentages.
- Final suite: 914 per configuration, zero failures. Passed/skipped:
elevated Desktop 890/24, Core 860/54; basic Desktop 749/165, Core 719/195.
- NUnit skipped ForEach names retain placeholders and parameter tuples,
executed names expand them. Strip trailing data tuples and match templates;
raw-name intersection or positional alignment is invalid across editions.
139 skipped templates have eligible executed counterparts. Inspect input
eligibility when an individual data row has a condition of its own.
- Remaining inventory: 918 points, including 244 in cmdlet-unused classes,
112 parameter-getter points, and 562 awaiting finer classification/testing.
Preserve raw XML, eligibility CSV, logs, commit identity, and build hashes.
## Lab acceptance
- Defaults: F1ADC1 (domain), F1AFile2 (server), F1AFile1 (client), all in
a.forest1.net. Foreign accounts use F1BDC1, F2DC1, F3DC1 and existing trusts.
Controller accepts alternate machines; changing topology/OS scope waits
for a maintainer decision. Do not repurpose another project's shared VMs.
- Before a run: authenticated WinRM, LDAP RootDSE, Kerberos tickets, member
secure channels, clocks; checkpoint only approved targets. Inspect actual
checkpoint kind: new checkpoints reported Standard even after a successful
temporary ProductionOnly request. Policy restored; no rollback performed;
Production classification remains unverified, not a passed safety check.
- Run Tests\Lab\Invoke-NTFSSecurityLabTest.ps1 in elevated Desktop with
-Version for hash-checked Gallery packages or -ModulePath for the extracted
build artifact, both editions. Per version/edition: Delegate, ServerAdmin,
Admin on client, then Server independently checks persisted state.
- Controller writes Summary.json even when tests fail: validate every role,
exit code, failure name, and total; DONE alone is not acceptance evidence.
Desktop ConvertFrom-Json can wrap arrays; explicitly enumerate the result
and compare full Describe-prefixed names. Never gate cleanup on the global
Error.Count, which includes handled errors; independently verify footprint.
- Remote Authz answers administrators and Access Control Assistance
Operators (S-1-5-32-579); other accounts get access denied. Check firewall
when remote resource-manager RPC fails. Expected rights use S4U tokens.
- RemoveFixture after the run; verify OUs/accounts, share, folders, local
memberships, and test profiles removed. Credentials must never be printed.

Loading…
Cancel
Save