Skip to content

Say precisely what GetFullPathNameW does, and why it stays - #86

Merged
MikeGrier merged 36 commits into
mainfrom
mikegrier/namespace-full-path-doc
Sep 10, 2026
Merged

MikeGrier merged 36 commits into
mainfrom
mikegrier/namespace-full-path-doc

Conversation

@MikeGrier

@MikeGrier MikeGrier commented Sep 10, 2026

Copy link
Copy Markdown
Owner

Peels the GetFullPathNameW correction ("G") out of #56, and completes M2.6.

This description has been rewritten. The version it replaces asserted, in bold, that "touches no filesystem" is the claim that holds -- which this PR went on to measure false. Leaving that standing while the diff below disproves it would have been one more instance of the defect the PR is about, so the record is corrected here rather than only in the code.

The defect

full_path.rs said, in two consecutive sentences:

This call is lexical. It resolves relative components and ./.. against the process current directory[.]

Those disagree. A lexical canonicalizer is a pure function of its input; this reads mutable process state -- the current directory, and for a drive-relative path such as C:foo the per-drive current directory Windows keeps in the hidden =C: environment variables.

That was the entry point. The subject turned out not to be one wrong word.

What the call actually does

Measured by P/Invoke, not read off documentation:

  • It does not verify what it produces. That is the documented guarantee, and it is narrower than it sounds. "Touches no filesystem" is false, not merely unproven.
  • Two separable jobs. Collapsing ./.. is pure string work that reads no process state (C:\a\..\b is C:\b under any current directory). Only rooting reads process state.
  • A drive-relative path touches the filesystem and writes the environment. Resolving X:foo for a non-current drive validates the =X: entry against both a shape and existence -- an existing file is rejected, as is a missing directory, C:/..., ...\., ...\..\... and \\?\... -- and an entry that fails is written back as the drive root, created when absent, so this happens on a pristine host on every call. An accepted entry is used verbatim, even onto another drive, and literally: C:\Windows\ yields C:\Windows\\foo.
  • For the current drive, neither happens -- the entry makes no difference to the result and is not rewritten. (Whether it is read is deliberately not claimed: installing a value and watching the outcome cannot separate "not read" from "read and ignored".)
  • A legacy device name short-circuits rooting entirely -- and NUL does so even from a fully qualified path (C:\NUL resolves to \\.\NUL, where every other device word roots normally once anything precedes it). CON, CON:, CON., con, CONIN$ and COM/LPT plus one digit -- including the superscripts U+00B9/B2/B3, which a hand-written denylist misses.
  • Cost. The probe's build - clone gap (~165 ns) is an upper bound spanning one net allocation and a builder chain; the call alone measures ~110 ns.

The decision, recorded as D-18

A genuinely lexical canonicalizer exists -- PathCchCanonicalizeEx, or PathAllocCanonicalize.

It is the wrong call here, and the reason is the property rather than the price. Resolving against the current directory at submission is what this crate is buying. A lexical canonicalizer would leave a relative path relative, so its meaning would be settled on the worker at execution time, against a directory any thread may have changed in between -- reintroducing exactly the race preparation exists to close. No cost claim is made about the alternatives, because none was measured.

Whether it enters the kernel is still not established, and the earlier argument for that -- "both are ordinary process memory" -- has been withdrawn. The reasoning was sound and the conclusion was false: the drive-relative form queries the filesystem and writes the environment block.

The real subject: claims that outrun their evidence

The PR ran to fourteen review rounds, and nearly every one found the fix for the previous round committing a fresh instance of the same error. That history is now the worked example, recorded in DESIGN-RATIONALE.md: nine successive wrong drafts, each naming a mechanism the evidence did not reach.

The sharpest instance is not about GetFullPathNameW at all. A test helper folded "empty =X: value" into "absent entry", in a comment that called the equivalence measured. The measurement had never cleared the last error -- the only thing that distinguishes them -- so it could read nothing but what an earlier call left behind. Cleared and re-measured, they are distinct. The consequence was live: the guard that restores a borrowed entry on unwind deleted an inherited empty one, destroying the state it exists to preserve.

So: "measured" is itself a claim, and a procedure can be wrong in ways its result never reveals.

The sweep

M2.6 named one file. Sweeping found ten statements of the same fact in six files -- including one in a file an earlier commit had already edited, and one used as a live premise inside an open milestone in the root CHECKLIST. The reported site is a sample, not the population.

Deliberately not touched: the 2026-08-27 design session uses "lexical canonicalisation" generically and correctly, and a Tier 3 transcript is a raw record rather than a normative statement.

Scope

27 commits. Not documentation-only, and the earlier claim that it was is corrected here: the branch adds executable tests and a [dev-dependencies] windows-sys entry (Win32_Foundation, Win32_System_Environment) needed because std::env cannot address a key containing =. Commit types are scoped accordingly -- docs, test, and two fix commits in windows-namespace-request-sys plus one in the probes crate. The library's public API and behaviour are unchanged; every code change is in tests, comments, or the probe's report text.

234 lib tests, 37 doctests, 20 + 11 acceptance tests, and 158 probe tests pass; clippy --all-targets, rustfmt, rustdoc and the encoding gate over 629 files are clean.

Mike Grier and others added 3 commits September 9, 2026 22:32
… it stays

The module doc claimed the call is lexical and then, in the next sentence,
described it resolving against the process current directory. Those
disagree: a lexical canonicalizer is a pure function of its input, and this
reads mutable process state -- the current directory, and for a
drive-relative path the per-drive current directory in the hidden `=C:`
environment variables. "Touches no filesystem" is the claim that holds.

Swept `lexical` across the workspace rather than fixing the reported site:
6 statements in this crate, of which the probes checklist item named one.
The identical sentence was also in path.rs, with four more restatements in
doc examples, tests, an acceptance comment and DESIGN-NOTES. One further
site in windows-platform-probes is left to its own commit, since this crate
is release-managed and that one is not this crate's to change.

D-18 records the decision half. A genuinely lexical canonicalizer exists --
PathCchCanonicalizeEx, or PathAllocCanonicalize -- and is cheaper, but it is
the wrong call: resolving against the current directory AT SUBMISSION is the
property this crate buys, and a lexical call would leave a relative path
relative for a worker to resolve later against a directory any thread may
have changed. It also records that whether the call enters the kernel is
NOT established -- the PEB and environment block are process memory, which
is a statement about the data sources, not a measurement.

Completed item: M2.6 (part 1 of 2): Say precisely what `GetFullPathNameW`
does, in the crate that owns it, and decide whether it is still the call
`prepare` wants.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…sion

The workspace sweep for `lexical` found one site in this crate that the
namespace-request correction could not touch: device_map's printed prose
called path preparation "lexical resolution" while explaining why it does
not close the drive-letter hazard. The sentence is right about the hazard
and wrong about the mechanism, in the same direction as the two corrections
before it.

request_cost's module doc already declined to name a mechanism, which was
honest but left the question open one layer down. It now links D-18 in the
owning crate, which closes it: what the call does, why it is kept over the
cheaper PathCchCanonicalizeEx, and that whether it enters the kernel is not
established.

Completed item: M2.6 (part 2 of 2): Say precisely what `GetFullPathNameW`
does, in the crate that owns it, and decide whether it is still the call
`prepare` wants.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The two commits before this corrected the crate that owns the call and the
probe that consumes it. Sweeping `lexical` across the whole workspace found
four more statements of the same wrong fact, three of which neither commit
touched -- including one in a file the first commit had already edited.

- `full_path/tests.rs` still said "the call is lexical" in a second comment
  forty lines below the one that was fixed. The reported site is a sample,
  not the population, and that holds even when the population is one file.

- The root DESIGN-NOTES stated it as a premise of a design corollary. The
  corollary's conclusion is right and unchanged -- a drive letter survives
  submission-time canonicalisation -- but it was resting on a false reason,
  so it now gives the true one and links D-18 for the rest.

- Root CHECKLIST.md M20 used it as a live premise inside an OPEN milestone,
  which is the most expensive of the four: a reader executing that work
  would have inherited the wrong reason for a correct conclusion.

- CHECKLIST-thread-ambient.md stated it twice, in the entry catalogue table
  and in M26.5. Both are completed records, and the correction is confined
  to the technical premise; the request each recorded is untouched, and
  M26.5's is in fact what D-18 now satisfies.

Deliberately not touched: the 2026-08-27 design session uses "lexical
canonicalisation" generically and correctly, and a Tier 3 transcript is a
raw record rather than a normative statement.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings September 10, 2026 02:32

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The changes are documentation/design-note corrections with a consistent workspace-wide sweep and no remaining inconsistencies found in the updated hunks.

Pull request overview

This PR corrects and precisely documents GetFullPathNameW semantics (not “lexical”, and “touches no filesystem” vs. “reads mutable process state”), and records the decision to keep it because submission-time resolution is the property the crate needs.

Changes:

  • Updates windows-namespace-request-sys docs (and its design notes) to state exactly what GetFullPathNameW does, why it remains the right call, and names the genuinely-lexical alternatives.
  • Sweeps and corrects restatements across the workspace (design notes, checklists, probe docs, tests/examples) to remove the incorrect “lexical” claim.
  • Marks the windows-platform-probes checklist item M2.6 complete and links readers to the owning-crate decision (D-18) instead of re-deriving assumptions in the probe.
File summaries
File Description
DESIGN-NOTES.md Updates the root design note to remove the incorrect “lexical” rationale and point to windows-namespace-request-sys decision D-18.
crates/windows-platform-probes/src/request_cost.rs Adds a pointer to the owning crate’s D-18 decision to avoid probe-side re-derivation.
crates/windows-platform-probes/src/bin/device_map.rs Rewords output text to remove the incorrect “lexical resolution” phrasing.
crates/windows-platform-probes/CHECKLIST.md Marks M2.6 as completed.
crates/windows-namespace-request-sys/tests/acceptance/operations.rs Updates an acceptance comment to remove “lexical” wording.
crates/windows-namespace-request-sys/src/path.rs Corrects the crate docs to state “touches no filesystem” and “not lexical”, including the process-state inputs.
crates/windows-namespace-request-sys/src/full_path/tests.rs Updates test docs/comments to remove the “lexical” claim and keep the “touches no filesystem” property.
crates/windows-namespace-request-sys/src/full_path.rs Expands and clarifies the module docs: what it solves, why not the lexical alternatives, and what is/ isn’t established about kernel transitions.
crates/windows-namespace-request-sys/DESIGN-NOTES.md Adds D-18 capturing the correction, the “keep GetFullPathNameW” decision, and the named alternative(s).
CHECKLIST.md Updates M20.1 premise to remove “lexical” and retain the true invariant (“never expands a drive letter”).
CHECKLIST-thread-ambient.md Updates the audited call table and M26.5 record to remove “lexical” and preserve the correct properties.
Review details
  • Files reviewed: 11/11 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Several statements exceed the documented Win32 contract, make unmeasured cost claims, and leave completed checklist records unarchived.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (4)

crates/windows-namespace-request-sys/src/full_path/tests.rs:50

  • This test only establishes that the resulting path need not exist; it cannot establish that the API never accesses the filesystem. Phrase the comment in terms of the behavior under test.
    // The call touches no filesystem. A consumer wanting a verified path wants
    // an open plus GetFinalPathNameByHandleW.

crates/windows-namespace-request-sys/src/full_path.rs:147

  • The example should not claim that no filesystem access occurs when the documented contract only guarantees no validity/existence check.
/// // `.` and `..` are resolved without touching the filesystem.

crates/windows-namespace-request-sys/src/full_path.rs:164

  • Resolving a nonexistent path proves that existence is not checked, but not that the function performs no filesystem access. Use the actual documented guarantee here.
/// // A path to nothing resolves perfectly happily, because the call
/// // touches no filesystem. A consumer wanting a verified path wants an open plus
/// // GetFinalPathNameByHandleW instead.

CHECKLIST-thread-ambient.md:423

  • This completed record now embeds the same unsupported zero-filesystem-access claim. The documented behavior is that the result is not checked for validity or existence.
- [x] **M26.5** -- The `GetFullPathNameW` entry. Touches no filesystem: it resolves relative components
  and `.`/`..` against the process current directory, and never expands a drive letter, so it does
  **not** close the session-relative hazard from M20.1, and its documentation must say which problem it
  solves and which it leaves standing.
  • Files reviewed: 11/11 changed files
  • Comments generated: 11
  • Review effort level: Balanced

Comment thread CHECKLIST-thread-ambient.md Outdated
Comment thread CHECKLIST-thread-ambient.md Outdated
Comment thread crates/windows-namespace-request-sys/DESIGN-NOTES.md Outdated
Comment thread crates/windows-namespace-request-sys/DESIGN-NOTES.md Outdated
Comment thread crates/windows-namespace-request-sys/DESIGN-NOTES.md Outdated
Comment thread crates/windows-namespace-request-sys/src/full_path.rs Outdated
Comment thread crates/windows-namespace-request-sys/src/full_path/tests.rs Outdated
Comment thread crates/windows-namespace-request-sys/src/path.rs Outdated
Comment thread crates/windows-platform-probes/CHECKLIST.md Outdated
Comment thread crates/windows-platform-probes/src/request_cost.rs Outdated
Self-review caught this, and it is the same defect D-18 exists to correct,
committed inside the paragraph warning against it.

The doc said `probe-request-cost` "measures roughly 212 ns per resolution".
It does not. The probe reports `prepare_short_path` as an explicit
construct-and-drop cycle -- an allocation, this resolution and a drop
together -- and says in its own output that it declines to say what the
total is made of. Attributing it to the resolution names a mechanism the
measurement does not isolate, which is exactly what "a syscall cost" and
"lexical" each did before it.

Verified by running the probe rather than by reading the number: it prints
208.0 ns for `prepare_short_path` on this x86_64 host under the heading
"the first four are construct-and-drop cycles". The figure now bounds the
call from above and claims nothing else.

Swept both restatements. The probes CHECKLIST said "~212 ns for a build ...
and declines to say what that is made of", which was accurate already; it
was the restatement in this crate that drifted, in both the module doc and
D-18.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 10, 2026 02:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Unsupported cost claims and repository documentation/checklist conventions remain unresolved.

Review details

Suppressed comments (5)

crates/windows-namespace-request-sys/src/full_path.rs:35

  • The claim that either alternative is cheaper is not established here: the cited probe measures GetFullPathNameW only, Microsoft documents no comparative cost, and PathAllocCanonicalize performs its own allocation. Since the decision depends only on avoiding process-state reads, remove the performance claim unless a direct measurement is added.
//! One exists: `PathCchCanonicalizeEx`, or `PathAllocCanonicalize`. Either is
//! cheaper and reads no process state at all.
//!
//! **They are the wrong call here, and the reason is the property above rather

crates/windows-namespace-request-sys/DESIGN-NOTES.md:558

  • This decision repeats an unverified performance conclusion. The available probe does not benchmark either canonicalizer, and the platform documentation specifies behavior rather than relative cost. State the relevant property—no process-state lookup—without calling the alternatives cheaper.
**The decision: keep `GetFullPathNameW`.** A genuinely lexical canonicalizer
exists -- `PathCchCanonicalizeEx`, or `PathAllocCanonicalize` -- and is cheaper,
reading no process state at all. It is the wrong call here, and for the property
rather than the price: resolving against the current directory *at submission* is
what this crate is buying. A lexical canonicalizer would leave a relative path

crates/windows-platform-probes/src/request_cost.rs:83

  • Calling the alternative “cheaper” reintroduces an unsupported mechanism/cost claim into the probe documentation. Neither alternative is measured by this probe, so describe it only as lexical or process-state-independent.
//! `D-18`, which states what the call actually does, records keeping it over
//! the cheaper lexical alternative, and says plainly that whether it enters the
//! kernel is not established.

crates/windows-platform-probes/CHECKLIST.md:91

  • Checking this multi-line item leaves a large completed item in the active checklist. The repository’s completed-item rule requires moving an item over 100 characters (or with sub-items) immediately to COMPLETED-CHECKLIST.md and replacing it here with the one-line anchored completion stub.
- [x] **M2.6** -- Say precisely what `GetFullPathNameW` does, in the crate that owns it, and decide

crates/windows-namespace-request-sys/DESIGN-NOTES.md:537

  • D-18 adds a new decision but keeps its correction history, sweep narrative, and rejected first-draft reasoning entirely in Tier 1. The repository’s design-document rules require new decisions in a component with substantial design history to record rationale in DESIGN-RATIONALE.md in the same commit; keep the current decision and constraint here, and move/link the historical material to Tier 2.
## <a id="d-18"></a>D-18: `GetFullPathNameW` is not lexical, and the genuinely lexical alternative is the wrong call
  • Files reviewed: 11/11 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Independent review of the previous commits found the correction had
overshot: saying the call resolves `.`/`..` "against the process current
directory" attributes process-state dependence to the one operation that
has none. That is the same imprecision D-18 was written to fix, running
the other way.

Measured rather than reasoned. With the process CWD set to C:\Windows and
then C:\Users, a fully-qualified `C:\a\..\b` resolves to `C:\b` both
times, while `rel.txt` follows the CWD. So the call does two separable
things: it collapses `.`/`..` and normalizes separators, which IS lexical
and reads no process state; and it roots a path that is not fully
qualified, which reads the current directory or the drive's own `=C:`
entry. It is not lexical AS A WHOLE, and only the rooting half is the
property submission-time resolution buys -- which is what D-18's decision
actually turns on.

Corrected in all four places that had merged the two: the module doc,
path.rs, the root DESIGN-NOTES and M26.5.

Two further review findings:

- D-18 undercounted its own sweep as "six places" and "four further
  restatements". The real figure is nine sites across five files, three
  of them in full_path.rs alone. A paragraph whose point is "the reported
  site was a sample, not the population" should not itself undercount the
  population.

- M2.6 was checked off while its body still read as pending -- quoting
  the module doc text this change deletes, and saying the correction "is
  queued rather than taken". Rather than restate it in the past tense,
  the item is archived per the completed-item rule (2411 characters, so
  large) with a one-line stub pointing at it, and the archive records the
  resolution above the request it preserves.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 10, 2026 02:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The documentation asserts an unmeasured performance ordering and has unresolved design-record formatting and rationale issues.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (3)

crates/windows-namespace-request-sys/src/full_path.rs:42

  • Calling both alternatives “cheaper” is an unsupported performance claim: the only measurement documented below is the current construct-and-drop path, and PathAllocCanonicalize performs its own allocation. The semantic argument does not need this claim; describe only the observable contract unless an isolated comparison is added.
//! One exists: `PathCchCanonicalizeEx`, or `PathAllocCanonicalize`. Either is
//! cheaper and reads no process state at all.

crates/windows-namespace-request-sys/DESIGN-NOTES.md:566

  • This repeats the unmeasured “cheaper” comparison as part of the canonical decision. The probe explicitly does not isolate this call, much less compare it with either PathCch API, so retain the relative-path semantics that decide the choice and remove the cost assertion.
**The decision: keep `GetFullPathNameW`.** A genuinely lexical canonicalizer
exists -- `PathCchCanonicalizeEx`, or `PathAllocCanonicalize` -- and is cheaper,
reading no process state at all. It is the wrong call here, and for the property

crates/windows-platform-probes/src/request_cost.rs:83

  • This cross-reference turns the unmeasured “cheaper” comparison into probe documentation. D-18’s decisive fact is that the alternative leaves relative input relative; refer to that contract rather than asserting a performance ordering the probe never measured.
//! `D-18`, which states what the call actually does, records keeping it over
//! the cheaper lexical alternative, and says plainly that whether it enters the
//! kernel is not established.
  • Files reviewed: 12/12 changed files
  • Comments generated: 4
  • Review effort level: Balanced

Comment thread crates/windows-namespace-request-sys/DESIGN-NOTES.md Outdated
Comment thread crates/windows-namespace-request-sys/DESIGN-NOTES.md Outdated
Comment thread crates/windows-platform-probes/COMPLETED-CHECKLIST.md Outdated
Comment thread crates/windows-platform-probes/COMPLETED-CHECKLIST.md Outdated
…t cost

Second review round. The previous commit over-corrected the first one, in
the opposite direction, and both errors were about the same number.

The first draft called ~210 ns a per-resolution cost, attributing to this
call a total that also contains an allocation and a drop. Correcting that,
the second draft said the probe "declines to decompose" the total. It does
decompose: the report states that recycling an already-resolved path pays
~42 ns against ~210 ns, and that the ~168 ns difference is the resolution
rather than the allocation -- which is exactly why recycling is the only
one of its two candidate optimizations that can touch it. What the probe
withholds is the MECHANISM, not the division.

So the doc now quotes ~168 ns as this call's measured share, and says the
number is silent on whether any of it entered the kernel.

Also corrects which label the ~210 ns belongs to. It is the whole request
build; `prepare_short_path` is a different, lower figure. Measured over
three runs on this x86_64 host: prepare 200-206 ns, build 208-218 ns,
clone 43-69 ns. An earlier single run had read 287 ns for the build, which
was noise -- these probes are not deterministic, which is why the repo's
own control for them exists.

Two further fixes:

- The two-part split omitted a third rooting form. `\foo` takes only the
  current DRIVE from process state, yielding `C:\foo` -- not the current
  directory's subtree, which the previous wording implied. Measured under
  two different current directories. The lexical half now also names
  trailing dot/space trimming and legacy device mapping (`CON` ->
  `\\.\CON`), which a path-preparing crate's callers can hit.

- D-18's sweep narrative counted the doc examples twice. The nine/three/two
  totals were right; the breakdown now adds to nine.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 10, 2026 03:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The documentation makes unsupported performance attributions and does not follow the repository’s design and checklist archival conventions.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (7)

crates/windows-namespace-request-sys/src/full_path.rs:48

  • The “cheaper” comparison is not established: the probe never invokes either PathCch alternative, and the API documentation makes no relative-performance guarantee. Since this change is specifically avoiding mechanism claims beyond the evidence, please remove the cost claim (or add an actual benchmark) and keep the decision based on the required rooting behavior.
//! One exists: `PathCchCanonicalizeEx`, or `PathAllocCanonicalize`. Either is
//! cheaper and reads no process state at all.

crates/windows-namespace-request-sys/DESIGN-NOTES.md:566

  • The new decision records “cheaper” as a fact, but no benchmark in this repository calls either PathCch alternative, and their API contracts provide no comparative performance guarantee. Remove the unsupported cost conclusion or benchmark the alternatives; the current-directory-rooting requirement is sufficient to justify the decision.
**The decision: keep `GetFullPathNameW`.** A genuinely lexical canonicalizer
exists -- `PathCchCanonicalizeEx`, or `PathAllocCanonicalize` -- and is cheaper,
reading no process state at all. It is the wrong call here, and for the property

crates/windows-platform-probes/src/request_cost.rs:83

  • This repeats the unmeasured assertion that the lexical alternative is cheaper. The probe does not call PathCchCanonicalizeEx or PathAllocCanonicalize; it only compares building a request with cloning an existing path. Describe the alternative by its established behavior, not by an unsupported relative cost.
//! The owning crate now settles both halves rather than leaving them to be
//! re-derived from a probe: see `windows-namespace-request-sys`'
//! [DESIGN-NOTES.md](../../windows-namespace-request-sys/DESIGN-NOTES.md) ->
//! `D-18`, which states what the call actually does, records keeping it over
//! the cheaper lexical alternative, and says plainly that whether it enters the
//! kernel is not established.

crates/windows-platform-probes/COMPLETED-CHECKLIST.md:104

  • The resolution summary turns the unmeasured “cheaper” premise into a completed finding. No probe here invokes PathCchCanonicalizeEx, so the archive should record only the established semantic difference unless a comparative benchmark is added.
`GetFullPathNameW` collapses `.`/`..` lexically but roots a path that is not fully qualified against
process state, so it is not a lexical call as a whole; `PathCchCanonicalizeEx` is genuinely lexical,
cheaper, and the wrong call, because rooting at submission is the property being bought. Whether it
enters the kernel is recorded as NOT established. The item's body below is the request as it was

crates/windows-platform-probes/COMPLETED-CHECKLIST.md:97

  • This archive entry does not follow the repository’s completed-item format (.github/copilot-instructions.md:1398-1412): the date group must be a ## heading, followed by a separate ### anchored item heading that repeats the stub summary and carries the inline completion timestamp. Keeping that shape makes the stub target and archive index consistent with the other completed items.
## <a id="m26"></a>Moved 2026-09-09 22:54:01 -04:00 -- M2.6: what `GetFullPathNameW` does, and whether it stays

crates/windows-namespace-request-sys/DESIGN-NOTES.md:537

  • This new D-18 section mixes the current decision with draft history and alternative-analysis prose, but the repository’s design-document convention requires new decisions to be written to both Tier 1 DESIGN-NOTES.md and Tier 2 DESIGN-RATIONALE.md in the same commit (.github/copilot-instructions.md:1495-1512). Please keep a compact canonical decision here and add the historical reasoning to a cross-referenced Tier 2 file.
## <a id="d-18"></a>D-18: `GetFullPathNameW` is not lexical, and the genuinely lexical alternative is the wrong call

crates/windows-namespace-request-sys/DESIGN-NOTES.md:560

  • Repository documentation requires file references to be clickable links. path.rs is the only explicit file name in this inventory left as inline code; link it like the adjacent full_path.rs and tests.rs references.
with two more in `path.rs`, two in [tests.rs](src/full_path/tests.rs), one in an
  • Files reviewed: 12/12 changed files
  • Comments generated: 3
  • Review effort level: Balanced

Comment thread crates/windows-namespace-request-sys/DESIGN-NOTES.md Outdated
Comment thread crates/windows-namespace-request-sys/src/full_path.rs Outdated
Comment thread crates/windows-platform-probes/CHECKLIST.md Outdated
…aims

Third review round. The pattern is now explicit in D-18: each of the three
previous drafts of the cost paragraph named a mechanism the evidence did
not reach, in a different way.

COST. The last draft subtracted `clone_prepared_units` from
`build_open_request` and called the ~168 ns remainder "this call's measured
share". The subtraction does not isolate the call. `build_open_request`
runs the whole preparation step plus the OpenFile builder chain, and
`prepare` makes TWO heap allocations of its own -- a copy of the input and
a MAX_PATH output buffer -- against the clone's one. So the gap is
dominated by allocator work the same sentence was busy excluding.

Measured, five runs on this x86_64 host: prepare 211.5, build 218.4,
clone 47.0, so the builder chain is ~7 ns and the gap ~165 ns (the 168 was
subtracting two separately rounded figures). Timed directly with the input
marshalled and the output buffer pre-allocated, so no allocation is in the
loop, the call itself is ~110 ns over 200k iterations -- about two thirds
of the gap. The doc now states the probe's number as an upper bound, says
plainly that no instrument here isolates the call, and labels the direct
measurement as not being a probe output.

PATHS. Three further corrections, each measured:

- A legacy device name SHORT-CIRCUITS the rooting rather than adding to
  the rewriting. `CON` -> `\\.\CON` and is not rooted at all, so it was a
  counterexample to the unconditional "roots a path that is not fully
  qualified" and to the claim that unqualified inputs vary with process
  state -- `CON` is unqualified and invariant. It is exact-match only:
  `CON.txt` and `a\CON` root normally, and `\CON` -> `Q:\CON`.

- `\foo` takes the ROOT of the current directory, not its drive. With the
  current directory set to \\localhost\C$\Windows it yields
  \\localhost\C$\foo, which has no drive letter at all. This crate handles
  \\?\UNC\ paths, so the UNC case is in scope.

- The root DESIGN-NOTES and M26.5 still said rooting was "against the
  process current directory", which the drive-relative case disproves:
  setting =C: while leaving the current directory alone changes what
  `C:foo` resolves to. Both now match D-18 and path.rs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 10, 2026 03:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The documentation includes version-dependent device-name behavior and unsupported performance claims, alongside required design/checklist formatting issues.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (8)

crates/windows-namespace-request-sys/src/full_path.rs:56

  • Neither the Windows contract nor any repository benchmark establishes that these alternatives are cheaper; reading less process state does not by itself prove lower runtime cost, and PathAllocCanonicalize allocates its result. Remove the performance claim and keep the property-based rationale.
//! One exists: `PathCchCanonicalizeEx`, or `PathAllocCanonicalize`. Either is
//! cheaper and reads no process state at all.

crates/windows-namespace-request-sys/DESIGN-NOTES.md:566

  • The decision repeats an unverified performance claim: no direct comparison in this repository establishes that either lexical API is cheaper, and PathAllocCanonicalize also performs allocation. The keep/remove decision already rests on semantics, so state only that the alternatives omit current-directory rooting.
**The decision: keep `GetFullPathNameW`.** A genuinely lexical canonicalizer
exists -- `PathCchCanonicalizeEx`, or `PathAllocCanonicalize` -- and is cheaper,
reading no process state at all. It is the wrong call here, and for the property

crates/windows-platform-probes/src/request_cost.rs:83

  • This propagates the unsupported claim that the lexical alternative is cheaper. The probe does not compare these APIs, and reduced process-state access is not a performance measurement; describe it simply as the lexical alternative.
//! [DESIGN-NOTES.md](../../windows-namespace-request-sys/DESIGN-NOTES.md) ->
//! `D-18`, which states what the call actually does, records keeping it over
//! the cheaper lexical alternative, and says plainly that whether it enters the
//! kernel is not established.

crates/windows-platform-probes/COMPLETED-CHECKLIST.md:103

  • The resolved summary presents PathCchCanonicalizeEx as cheaper even though no comparison in this PR or the probe establishes that. Remove the cost claim here as well so this restatement matches the property-based decision.
`GetFullPathNameW` collapses `.`/`..` lexically but roots a path that is not fully qualified against
process state, so it is not a lexical call as a whole; `PathCchCanonicalizeEx` is genuinely lexical,
cheaper, and the wrong call, because rooting at submission is the property being bought. Whether it

crates/windows-namespace-request-sys/DESIGN-NOTES.md:537

  • This adds a new canonical decision but no Tier 2 rationale. The repository design-document rule requires new decisions to be recorded in both DESIGN-NOTES.md and DESIGN-RATIONALE.md in the same commit; this component has substantial design history, so add the rationale file/section and cross-link D-18.
## <a id="d-18"></a>D-18: `GetFullPathNameW` is not lexical, and the genuinely lexical alternative is the wrong call

crates/windows-namespace-request-sys/DESIGN-NOTES.md:560

  • path.rs is a repository file reference but is left as inline code. The markdown cross-reference rule requires references readers should open to be clickable relative links.
with two more in `path.rs`, two in [tests.rs](src/full_path/tests.rs), one in an

crates/windows-platform-probes/COMPLETED-CHECKLIST.md:97

  • This archive entry does not follow the required completed-item structure: the date group must be an unanchored ## Moved ... heading, followed by a ### item heading carrying the m26 anchor, the same summary as the stub, and the inline completion timestamp.
## <a id="m26"></a>Moved 2026-09-09 22:54:01 -04:00 -- M2.6: what `GetFullPathNameW` does, and whether it stays

crates/windows-platform-probes/CHECKLIST.md:92

  • The completed-item rule requires the replacement stub to occupy one physical line. Keeping it on two lines defeats the compact active-checklist format used by the repository's existing completed stubs.
- [x] **M2.6** -- Say precisely what `GetFullPathNameW` does, in the crate that owns it, and decide
  whether it is still the call `prepare` wants. -> [completed 2026-09-09](COMPLETED-CHECKLIST.md#m26)
  • Files reviewed: 12/12 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread crates/windows-namespace-request-sys/src/full_path.rs Outdated
… own module

Fourth review round. Two findings, and they share a root cause: facts were
landing in whichever file was open rather than the file that owns them.

ROOTING IS NOT UNCONDITIONAL, AND FIVE SITES STILL SAID IT WAS. The previous
commit's own message called `CON` a counterexample to "roots a path that is
not fully qualified" -- then left that exact clause standing in path.rs, the
root DESIGN-NOTES, M26.5, D-18 and the probes archive, two of which it
rewrote in the same commit. The refuted statement outnumbered its correction
five to one. All now say "most paths".

THE DEVICE FORM IS LOOSER THAN "EXACT MATCH". Measured: `CON:`, `CON::`,
`NUL:`, `LPT1:`, `AUX:` map to the device namespace, as do `CON.`, `CON ` and
`con` -- the trailing colon is part of the form, the step-1 trimming runs
first, and matching is case-insensitive. Only a name with something after it
(`CON.txt`, `a\CON`, `CON:x`) roots normally. Saying "exact-match only" would
have told a reader `prepare("CON:")` gives a file path; it gives a device.

PLACEMENT. The `prepare("CON")` warning was in full_path.rs, which documents
`ResolveFullPath`; `prepare` lives in path.rs, whose own docs said nothing
about it. Moved. D-18, the durable record, mentioned neither the device
short-circuit nor the root-vs-drive distinction -- both existed only in a
module doc. Added.

Also mine, found while checking the reviewer's allocation claim: the cost
paragraph attributed "two heap allocations -- a copy of the input and a
MAX_PATH output buffer" to this crate while sitting in full_path.rs. That is
`path::resolve`'s shape, which is what the probe measures. `ResolveFullPath`
takes its input already owned and allocates one buffer per attempt, with a
growth loop `resolve` does not have. The paragraph now names which entry
point it is describing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 10, 2026 04:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new contract documentation has an internal sequencing contradiction and lacks required rationale separation and coverage for its platform-sensitive claims.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (1)

crates/windows-namespace-request-sys/DESIGN-NOTES.md:537

  • This new decision is recorded only in Tier 1 and embeds substantial draft history and alternatives. The repository convention requires new decisions to be written to both DESIGN-NOTES.md and DESIGN-RATIONALE.md; move the historical material (especially the draft chronology) to a new Tier 2 entry and keep D-18 focused on the current decision and constraints. See .github/copilot-instructions.md:1493-1512.
## <a id="d-18"></a>D-18: `GetFullPathNameW` is not lexical, and the genuinely lexical alternative is the wrong call
  • Files reviewed: 12/12 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread crates/windows-namespace-request-sys/src/full_path.rs Outdated
Comment thread crates/windows-namespace-request-sys/src/full_path.rs Outdated
…ontract, cover it with tests

Addresses the Copilot review feedback on #86, plus a fifth-round finding.

REMOVED THE "CHEAPER" CLAIM. Copilot flagged this in six separate reviews
and was right every time: nothing here benchmarks PathCchCanonicalizeEx or
PathAllocCanonicalize, Microsoft documents behaviour rather than relative
cost, and PathAllocCanonicalize allocates its own result. It was a guess
wearing the clothes of a measurement, in a decision whose entire subject is
not doing that -- and it survived four rounds of review because I kept
reading past it. The decision never needed it; rooting semantics decide it
alone. Also notes that PATHCCH_ALLOW_LONG_PATHS makes PathCchCanonicalizeEx
consult process long-path state, so "reads no process state" was wrong too.

NARROWED "TOUCHES NO FILESYSTEM". What Microsoft documents is that the
function does not verify the result is valid or names an existing file --
about verification, not I/O. Observation cannot close that gap: resolving
under a directory that does not exist shows no CHECK was made, not that no
filesystem was touched. Now stated as the documented guarantee at all seven
sites, which is what the crate actually relies on.

THE DEVICE SET WAS INCOMPLETE, AND SAID IT WAS CLOSED. COM/LPT accept
SUPERSCRIPT digits: COM^1, COM^2, COM^3 (U+00B9/B2/B3) and the LPT forms all
resolve to devices. My own earlier test appeared to show otherwise; re-run
with explicit code points it was unambiguous, and the first result had been
garbled console output I misread. Those are exactly the members a
hand-written denylist omits, and path.rs tells callers to guard untrusted
names -- so the doc now warns against building a filter from it. Also drops
the claim that trimming happens before the device check, which was an
undocumented ordering.

TESTS, so none of this rests on prose again. Seven new cases pin every
documented form: relative rooting, root-relative taking the ROOT (asserted
as a relation, so it holds under any current directory), the device
short-circuit, all its accepted spellings, the superscripts, the
rooted-normally control, and fully-qualified invariance. Verified
non-vacuous by injecting COM0 into the superscript case and watching it go
red.

CONVENTIONS. The M2.6 stub is one line; the archive has a date group plus an
anchored item heading with a completion stamp; and D-18's draft chronology
moves to a new Tier 2 DESIGN-RATIONALE.md, leaving Tier 1 the decision and
the constraint it carries.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Several summaries still omit the current-drive exception, and the PR description overstates whether the entry is consulted.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (5)

DESIGN-NOTES.md:1467

  • This summary omits D-18’s current-drive exception: X:foo uses the process current directory when X is the current drive; only another drive uses its recorded =X: entry. As written, this restates the contract too broadly.
  but roots most paths that are not fully qualified against process state -- the
  current directory, or for a drive-relative path the entry recorded for that
  drive, which moves independently of it -- and that rooting is the property

CHECKLIST-thread-ambient.md:423

  • The completed item still states the drive-relative rule without its current-drive arm. The new tests and D-18 show that the current drive uses the process directory and ignores the recorded entry; only another drive uses =X:.
- [x] **M26.5** -- The `GetFullPathNameW` entry. Does not verify its result: it collapses `.`/`..`
  lexically and roots most paths that are not fully qualified against process state -- the current
  directory, or for a drive-relative path the entry recorded for that drive -- and never expands a
  drive letter, so it

crates/windows-platform-probes/src/request_cost.rs:71

  • This decomposition again makes every drive-relative path sound as though it reads the per-drive entry. D-18 and the added test establish a separate current-drive arm that uses the process current directory instead, so this probe documentation should preserve that distinction.
//! That call reads **process state** when it has to root a path -- the current
//! directory, or for a drive-relative path the entry recorded for that drive in
//! the `=C:` environment variables. **Neither sample here is rooted**: both are

crates/windows-namespace-request-sys/DESIGN-RATIONALE.md:110

  • This historical summary says the final enumeration has three forms, but its drive-relative form still omits the measured current-drive branch. On the current drive the process directory wins; the recorded =X: entry is used only for another drive.
- **The rooting forms.** Stated first as one case (the current directory), then
  two, and finally three: a relative path takes the current directory, a
  root-relative path takes only that directory's *root* (which is
  `\\server\share\` under a UNC current directory, so "current drive" was
  wrong), and a drive-relative path takes the entry recorded for that drive
  from `=C:`.

crates/windows-namespace-request-sys/src/full_path/tests.rs:552

  • full_path.rs explicitly documents .\CON as rooting normally, but this exact-output test excludes it and the sibling negative test proves only that it does not start with \\.\. Returning .\CON unchanged would therefore pass both tests. Add an exact assertion for the collapsed-and-rooted result.
    // `.\CON` is excluded deliberately -- the `.` component collapses, so its
    // expected form is the bare name, which the loop below would have to
    // special-case. It is covered by the device-negative test instead.
  • Files reviewed: 22/22 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread crates/windows-namespace-request-sys/src/full_path.rs
Comment thread crates/windows-namespace-request-sys/src/full_path/tests.rs Outdated
…ng rule

The rule "a drive-relative path takes the entry recorded for that drive" is
stated in six places. The current-drive exception -- that the entry makes no
difference there and the process directory wins -- had been added to exactly
one of them, the owning crate's D-18, where it was discovered. The other five
kept the broad form, so the contract read differently depending on which file a
reader opened.

That is this repository's blast-radius rule failing on the change that
introduced the fact, not on inherited text. The measurement and the sweep landed
in different commits, which is the gap the rule exists to close.

Swept: the root DESIGN-NOTES corollary, CHECKLIST-thread-ambient M26.5, the cost
probe's decomposition, and the Tier 2 enumeration of the rooting forms -- which
also now records that "three forms" was itself one of the things stated more
confidently than measured, the current-drive arm being a fourth correction to
the same list. `path.rs` already had the narrow form and is unchanged.

The owning crate's own wording moves too, from "the entry is ignored" to "makes
no difference to the result", for the same reason the probe stopped saying
"without consulting a device": an entry read and discarded is indistinguishable
from one never read, and leaving one file saying "ignored" while another says
"makes no difference" is the drift this sweep is meant to remove.

Separately, `.\CON` was excluded from the exact-output device test on the
grounds that the `.` collapses and it would need a special case. It needed one,
so it has one. With the case left to the device-NEGATIVE test alone, the only
claim made about `.\CON` was that it does not begin `\\.\` -- which an
implementation returning it unchanged satisfies, and that too-weak assertion is
the exact thing the exact-output test was written to strengthen. Measured:
`.\CON` resolves to `<cwd>\CON`.

Verified: 234 lib + 37 doc + 32 integration tests, clippy --all-targets, cargo
fmt, encoding check over 630 files.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 10, 2026 18:37

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

New filesystem-sensitive unit tests bypass the suite’s handle-allocation lock, creating races with raw-handle tests.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (2)

crates/windows-namespace-request-sys/src/full_path/tests.rs:632

  • probe_directory creates/removes directories, and its callers also run exists/is_file and filesystem-querying drive-relative resolutions, but none takes the test suite's handle-allocation read lock. Concurrent raw-handle tests hold the write side specifically to prevent stale handle values from being reused (src/handle/tests.rs:25-31,263-274), so these helpers can invalidate that isolation. Make ProbeDir retain a read guard through its Drop, or have every caller hold one for the complete probe lifetime.
fn probe_directory(tag: &str) -> ProbeDir {
    // The full shape an accepted `=X:` entry must have, not just its first

crates/windows-namespace-request-sys/src/full_path/tests.rs:378

  • This other-drive resolution is now documented and tested as consulting the filesystem, but the test does not participate in handle_allocation(). It can therefore allocate a transient handle while a write-locked raw-handle test assumes no reuse is possible (src/handle/tests.rs:25-31,263-274). Acquire the read guard for the duration of this test.
fn a_drive_relative_path_is_rooted_at_that_drive_and_not_the_process_directory() {
    // The third rooting form. A drive-relative path is rooted at *that drive's*
  • Files reviewed: 22/22 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread crates/windows-namespace-request-sys/src/full_path/tests.rs
…t claims

Five local review rounds, one per model, squashed. Each reviewer found something
the others could not see, and every finding is the branch's own defect class:
a claim stated more precisely than the evidence supports.

**A fixture that deleted directories it did not create.** `probe_directory`
called `create_dir_all` and set `created: true` unconditionally; `Drop` removes
the path on that flag. `create_dir_all` succeeds on an existing directory, so
the flag recorded a claim nothing checked -- and the name `%TEMP%\wnrs-<pid>-<tag>`
is one an interrupted run leaves behind and Windows can hand to a later process
on PID reuse. Measured: `create_dir_all` on an existing directory returns
`Ok(())` and establishes nothing, while `create_dir` returns `AlreadyExists`.
Ownership is now recorded only on a real creation.

**Two tests that could not observe what they claimed.** The UNC fixture ran AT
`\\localhost\C$`, where rooting `\foo` at the ROOT of the current directory and
at the WHOLE current directory give the same answer -- so the only coverage of
the UNC branch could not see the branch, for six commits. The child now runs one
level down, asserts it is below the share root, and pins the contrasting rule
alongside. And `CON:x` was covered only by `!starts_with("\\.\\")`, a predicate
the unrooted literal satisfies; it is now asserted against its full rooted
result, `<cwd>\CON:x`.

**A generalisation that measurement contradicts.** "Trimming applies per
component" was extrapolated from one case. Measured, position decides: the final
component loses any run of trailing dots and spaces, an intermediate component
loses a single trailing dot and nothing else -- `C:\a...\b` and `C:\a \b` come
back unchanged. The preservation cases were entirely absent, so an
implementation trimming every component passed the whole test.

**Three location kinds a sweep kept missing.** Round 17 corrected "not
consulted" by grepping `consulted`, which cannot match the same claim spelled
`ignored`: six sites survived, one an assertion failure MESSAGE. A later round
fixed a paragraph explaining that a heading reading "not an allocation" would be
contradictory, and left that exact HEADING one line above it. A third left the
per-drive qualifier out of an INLINE COMMENT sitting between a module doc and
emitted output that both had it. All swept, this time across every changed file.

**Two properties restated in prose and checked nowhere.** The per-test drive
letter lists were literals at six call sites, with disjointness and minimum
length asserted only in comments -- and an archived note enumerated five lists
after a sixth existed. They are now one table with a test enforcing both
properties; the note points at the table instead of listing letters.

**A harness that trusted its own marker.** The UNC test treated the presence of
an environment variable as proof it was the child it spawned, so a stray value
made the parent run the child's assertions in cargo's directory and blame the
crate. The marker now carries the directory the parent chose.

Also: a test name claiming two things its `ends_with` assertion cannot reach,
renamed; `PLANS.md` giving one checklist two incompatible states, corrected; and
`request_cost`'s cross-host ratio claim -- real, pre-existing on `main`, outside
this diff -- queued as `M2.9` rather than folded in.

Every fix verified by sabotage: the instrument was made to fail on purpose, then
restored. Every Windows claim measured by P/Invoke rather than argued.

Verified: 235 lib + 37 doc + 32 integration tests, the UNC test under
--include-ignored, clippy --all-targets, cargo fmt, workflow references, and the
encoding gate over 630 files.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 10, 2026 20:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The UNC assertion is case-sensitive for a case-insensitive path, and one newly documented device-path boundary remains untested.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 22/22 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread crates/windows-namespace-request-sys/src/full_path/tests.rs
Comment thread crates/windows-namespace-request-sys/tests/unc_current_directory.rs
… it does not save

Copilot asked for the documented `\CON` case to be pinned, since the module doc
states it and no test covered it. Writing that assertion as the general rule the
request was phrased in -- "a root-relative device word roots at the current
directory's root" -- would have pinned a FALSE claim.

Measured across all eight accepted device names, `NUL` alone short-circuits as
the final component of any path, however much path precedes it:

  input      CON                NUL
  X          \\.\CON            \\.\NUL
  \X         Q:\CON             \\.\NUL
  .\X        Q:\...\CON         \\.\NUL
  a\X        Q:\...\a\CON       \\.\NUL
  C:\X       C:\CON             \\.\NUL
  X.txt      rooted             rooted
  X:x        rooted             rooted

`PRN`, `AUX`, `CONIN$`, `CONOUT$`, `COM1` and `LPT1` all behave as `CON` does.
So a FULLY QUALIFIED path can still resolve to a device -- `prepare(r"C:\NUL")`
is `\\.\NUL` -- and a caller treating a rooted path as evidence that a name
refers to a file on that volume is wrong for this one name. Only a suffix takes
it out.

**Why this was invisible for twenty-two rounds.** Every earlier finding in this
branch was a claim generalised from too few observations. This one was
generalised from one MEMBER to a set: the doc enumerates the device set, then
states the negative forms in terms of `CON`. The membership of that set was
audited twice -- it is where the superscript `COM` spellings were found -- and
the assumption that its members behave alike was never checked at all.

Pinned by `nul_is_the_one_device_word_a_path_around_it_does_not_save`, which
asserts the seven conforming names in both distinguishing forms and the four
`NUL` spellings that reach the device. `\CON` is now asserted against its full
rooted result, which is what was asked for. Verified by sabotage: replacing the
NUL expectation with the general rule -- the exact rule the request implied --
fails on `\NUL`, `\\.\NUL` against `q:\NUL`.

Swept into every restatement rather than the one file the finding named:
`full_path.rs` (with the table), `path.rs`'s public summary, `D-18`, and the
Tier 2 device-set entry.

**Declined, with the measurement.** The second comment read the UNC assertion's
case-sensitive comparison as a portability bug -- a host recording `LOCALHOST`
would fail against the lowercase literal. It cannot: both sides descend from
`UNC_ROOT`, since the parent builds the child's working directory from it.
Measured -- a process started with `\\LOCALHOST\C$\Windows` reports exactly that
from `current_dir()`, and likewise for `\\localhost\c$\Windows`, so Windows
preserves the spelling it is given rather than canonicalising it. Recorded in
place, with why the neighbouring precondition is case-insensitive for a
different reason.

Verified: 236 lib + 37 doc + 32 integration tests, clippy --all-targets, cargo
fmt, encoding over 630 files.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 10, 2026 20:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Some documentation still overstates what observation proves, one documented NUL boundary lacks coverage, and the enlarged test module needs splitting.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

crates/windows-namespace-request-sys/DESIGN-RATIONALE.md:37

  • Changing the current directory and observing the same result proves output invariance, not that the implementation “reads no process state at all.” Keep this rationale at the observable boundary, consistently with D-18’s treatment of the current-drive entry.
    crates/windows-namespace-request-sys/src/full_path.rs:39
  • The examples here establish that the output is invariant under the current directory; they do not establish the internal claim that the call reads no process state. That repeats the mechanism-overreach D-18 warns about. State the observable lexical semantics instead unless there is implementation-level evidence for the read claim.
  • Files reviewed: 22/22 changed files
  • Comments generated: 2
  • Review effort level: Balanced

//! So a *fully qualified* path can still resolve to a device, which is the
//! part worth knowing: `prepare(r"C:\NUL")` yields `\\.\NUL`, and a caller
//! treating a rooted path as proof it names a file on that volume is wrong for
//! this one name. Only a suffix (`NUL.txt`, `NUL:x`) takes it out.
Comment thread crates/windows-namespace-request-sys/src/full_path/tests.rs Outdated
… twenty-three rounds

Copilot found the overreach in the half nobody was auditing. Every correction in
this branch has been aimed at ROOTING -- "not consulted", "is ignored", "without
consulting a device". The same error was sitting in the sentence doing the
correcting: the LEXICAL half said collapsing `.`/`..` is "pure string work,
reading no process state".

Varying the current directory and getting the same answer shows the output does
not DEPEND on that state. It does not show nothing was read -- which is exactly
the argument this branch used to strike "not consulted" from the current-drive
entry, applied to the positive claim instead of the negative one. Seven
reviewers read that paragraph; it was invisible because it was the standard the
other corrections were measured against.

Both statements now say what the evidence reaches: the output is a function of
the input alone. That is also all a caller needs, since it is what lets this half
be reasoned about without knowing the process's state.

**And the NUL escape boundary, which the doc named and the test half-covered.**
The contract names `NUL.txt` and `NUL:x` as the spellings that take NUL out of
the device namespace; the new test asserted only the root-relative `\NUL.txt`.
Bare `NUL.txt` and `NUL:x` are now asserted, and so is the other side of the
boundary -- `NUL::` and `NUL ` still reach the device, so the rule reads as "a
suffix escapes" rather than "anything after NUL escapes". Measured: `NUL.txt`
and `NUL:x` root under the current directory, `NUL::` and `NUL ` give `\\.\NUL`.

Not taken here, and raised instead: this file is now 60.5 KiB, past the
repository's 32 KiB "large" threshold and close to the 64 KiB one where a split
becomes the default. The drive-entry fixture and its tests are a clean fracture
point. A split is a pure relocation with its own commit and provenance trailers,
so it does not belong inside a content change.

Verified: 236 lib + 37 doc + 32 integration tests, clippy --all-targets, cargo
fmt, encoding over 630 files.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 10, 2026 21:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The expanded full-path test module needs splitting at its clear drive-entry fracture point under the repository’s large-module rule.

Review details

Suppressed comments (1)

crates/windows-namespace-request-sys/src/full_path/tests.rs:707

  • This addition grows src/full_path/tests.rs to 60.5 KiB and leaves two clear responsibilities in one large test module: device/rooting behavior and the process-environment/drive-entry fixtures below. The repository's source-module-size rule says large test modules should split readily at such fracture points; please extract the drive-entry tests and helpers into a focused child module (for example, src/full_path/tests/drive_entries.rs) before this file reaches the 64 KiB XL threshold.
/// A directory that exists, is in canonical `X:\...` form, and is neither a
/// drive root nor the process current directory.
///
  • Files reviewed: 22/22 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

…ch layer the device rules describe

Opening an older review thread surfaced that Copilot's review BODIES only list a
subset of what it files -- twelve threads were open, several never seen here.
Four were live. Two are substantive.

**The probe fixture allocated handles outside the suite's serialisation.**
`handle_allocation()`'s read guard means "I may open handles"; its write guard
means "no other test may, while I reason about a specific handle value". Eleven
tests across four modules take the write guard for exactly that. `probe_directory`
called `create_dir`, `remove_dir`, `Path::exists` and `is_file` without the read
guard -- and `std`'s Windows metadata goes through `CreateFileW` -- so those
tests could run beside a write-guard holder and make its assertions fail for a
reason unrelated to what it pins. The guard is now held by `ProbeDir` for its
whole lifetime, which covers the fixture's own calls and the preconditions its
tests run while it is alive. One older test doing a bare `Path::exists` takes it
directly; it was the single filesystem call in the module the fixture does not
cover.

**A doc claim about the wrong layer, checked at both.** The review held that
saying `CON.txt` and `a\CON` "do not reach a device" gives callers a false
safety boundary, since Win32 reserves device basenames at open time. Measured at
the open rather than the resolver: creating `<dir>\NUL` returns a
`FILE_TYPE_CHAR` handle and leaves nothing on disk, while `<dir>\CON`,
`<dir>\CON.txt` and `<dir>\NUL.txt` each create an ordinary `FILE_TYPE_DISK`
file -- a real file named `CON` included. So the premise is false on this build:
the reservation lives in ROOTING, the two layers agree, and `NUL` reaches the
device at both.

The valid half is kept anyway, because agreement measured on one build is not a
guarantee this crate makes: the doc now says explicitly that those paragraphs
describe the RESOLVER's output, and that a caller sanitising untrusted names
should decide against what it will do with the result rather than infer
open-time safety from a resolved spelling.

Two stale restatements, both from earlier rounds of this branch: a comment still
calling the drive-letter candidates a "disjoint pair" after they became
three-element lists, and the probe's summary of the write-back stated
unconditionally when an accepted entry is left alone.

The PR description carried the same defect and is corrected in the same pass: it
still said the current drive's entry is "neither consulted nor rewritten", the
exact phrase the code moved away from, and now states the measured boundary and
the NUL exception.

Still open and not taken here: `full_path/tests.rs` is 62 KiB, past the 32 KiB
"large" threshold and near the 64 KiB one. The drive-entry cluster is a clean
fracture point, but a split is a pure relocation with its own commit and
provenance trailers and does not belong inside a content change.

Verified: 236 lib + 37 doc + 32 integration tests, clippy --all-targets, cargo
fmt, encoding over 630 files.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 10, 2026 21:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The enlarged test module needs splitting, and two checklist archive-format violations remain.

Review details

Suppressed comments (3)

crates/windows-platform-probes/COMPLETED-CHECKLIST.md:97

  • This moved-group heading uses a timestamp, while archive groups use a date-only heading and reserve the precise timestamp for the anchored item heading below. Keeping the date-group format consistent prevents one completed item from introducing a separate grouping key.
## Moved 2026-09-09 22:54:01 -04:00 -- M2.6: what `GetFullPathNameW` does, and whether it stays

crates/windows-namespace-request-sys/src/full_path/tests.rs:908

  • This material addition leaves tests.rs at 63,791 bytes, just below the 64 KiB threshold, and the file now has clear test-specific fracture points (rooting/device behavior versus the =X: fixture and drive-entry cases). Split those responsibilities into child test modules now; keeping the whole 1,436-line test module together makes the next small change cross the XL boundary and makes these independent fixtures harder to maintain.
mod probe_drives {

CHECKLIST-thread-ambient.md:424

  • This edited, checked item is still a multi-line historical body in the active checklist. Archive the completed M26.5 text and replace it here with the required one-line stub and stable item anchor, so the active checklist remains an action queue rather than retaining completed detail.
- [x] **M26.5** -- The `GetFullPathNameW` entry. Does not verify its result: it collapses `.`/`..`
  lexically and roots most paths that are not fully qualified against process state -- the current
  directory, or for a drive-relative path naming another drive the entry recorded for that drive,
  while on the current drive that entry makes no difference -- and never expands a
  drive letter, so it
  • Files reviewed: 22/22 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

…ive-entry arm

Pure relocation, no behaviour change: 236 lib tests before and after, and
`cargo fmt --check` is clean on the moved blocks.

full_path/tests.rs had reached 62.3 KiB -- past this repository's 32 KiB "large"
threshold and closing on the 64 KiB one where splitting becomes the default. The
per-drive current-directory (`=X:`) cluster is the fracture point: it is the only
part with a fixture of its own -- a probe directory, a drive-letter allocator, a
reader and writer for the hidden entries, and a guard that restores a borrowed
one -- and nothing outside it uses any of that.

The two drive-relative ROOTING tests moved with the fixture rather than staying
beside the other rooting forms, and that is a privacy constraint rather than a
preference. A child module can see its ancestors' private items; a parent cannot
see its child's. Left in the parent, those two tests could not reach the helpers,
so the cut would not have been viable as drawn. They are also the two tests about
this arm, so the constraint and the responsibility agree.

The mass moved rather than the periphery: 21.8 KiB stays in the facade against
41.5 KiB extracted, so the parent is back under "large" and the child is a single
coherent responsibility well clear of XL.

Only 18 lines are additions -- the provenance header, the module doc, `use
super::*;` and the `mod drive_entry;` declaration. Everything else is relocation,
which is what lets `git blame -w -C1 -C1` attribute the moved lines to the
commits that wrote them rather than to this one.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Split-Source: crates/windows-namespace-request-sys/src/full_path/tests.rs
Split-Into: crates/windows-namespace-request-sys/src/full_path/tests/drive_entry.rs
Copilot AI review requested due to automatic review settings September 10, 2026 21:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

A device-mapping assertion remains too weak, and several documentation references or contract restatements are stale.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (1)

crates/windows-namespace-request-sys/COMPLETED-CHECKLIST.md:44

  • The candidate table and its enforcing test were extracted to src/full_path/tests/drive_entry.rs; this link now opens the facade rather than the inventory described here.
  live in one table in [tests.rs](src/full_path/tests.rs) with a test enforcing
  • Files reviewed: 23/23 changed files
  • Comments generated: 4
  • Review effort level: Balanced

Comment thread crates/windows-namespace-request-sys/src/full_path/tests.rs Outdated
Comment thread crates/windows-namespace-request-sys/COMPLETED-CHECKLIST.md Outdated
Comment thread crates/windows-namespace-request-sys/DESIGN-RATIONALE.md Outdated
Comment thread crates/windows-platform-probes/COMPLETED-CHECKLIST.md
…point the links the split moved

Copilot's review of the split. Its two main asks were already done -- the split
itself, and the NUL suffix cases -- so the live findings are what it saw in the
new shape.

**A device assertion that only proved "some device".**
`a_legacy_device_name_short_circuits_rooting` asserted
`resolved.starts_with(r"\\.\")` over eight names, which would pass if `CON`
resolved to `\\.\NUL` -- and the contract names which device each spelling
reaches. Both device tests now assert the complete mapping. That also pins what
the device form does to a spelling: `CON:`, `CON::`, `CON.` and `CON ` all give
`\\.\CON`, while `con` and `cOn:` give `\\.\con` and `\\.\cOn`, so trailing
punctuation is dropped and case is carried through. Verified by sabotage --
expecting `\\.\NUL` for every name, which is Copilot's own scenario, now fails on
`CON`.

**Three links the split invalidated**, all mine: the archive's pointers to the
verbatim test and the candidate table, and Tier 2's pointer to the empty-entry
regression test, all now resolve to `drive_entry.rs`. Two others were checked and
left -- they cite tests that stayed in the facade.

Also: the probes archive stated the `=X:` write-back unconditionally, where an
accepted entry is left alone; and the `## Moved` group heading this branch added
carried a timestamp where the format reserves that for the anchored item heading.

**Declined, and queued as `M-inf.2` instead.** A comment asked for M26.5's
completed body in `CHECKLIST-thread-ambient.md` to become a one-line stub. The
rule is right and the file does violate it, but M26.5 is not exceptional --
stubbing only the reported item would have made it inconsistent with the five
siblings written the same way. Counted rather than assumed: every group in that
file is complete and due for migration -- M22 (8 items), M23 (6), M24 (6), M25
(7), M26 (6), M27 (6), M28 (4), M29 (5) -- with only `M26+` open. That is roughly
400 lines of another feature's bookkeeping, and moving it through a
`GetFullPathNameW` documentation branch would bury the change this branch exists
to make.

Verified: 236 lib + 37 doc + 32 integration tests, clippy --all-targets, cargo
fmt, encoding over 631 files.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 10, 2026 21:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The documentation, tests, planning records, and probe output are consistent, and the affected CI checks pass.

Review details
  • Files reviewed: 23/23 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@MikeGrier
MikeGrier merged commit a23d4bf into main Sep 10, 2026
26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants