-
Notifications
You must be signed in to change notification settings - Fork 0
m4 link state inspection
Status: Completed. Tracked by issue #6 (closed) and sub-issues #44 through #48
(closed, merged via PRs #96-#100). docs/adr-phase-3.md ADR-0014 through ADR-0017
record the implementation decisions.
M4 adds read-only inspection of Links\<alias>.exe and reports enough structured state
for the later CLI, TUI, and repair milestones. It covers:
- classification as
Ok,Missing,Broken, orMismatch; - decoding Windows symbolic-link reparse data;
- comparing the resolved target with the expected package executable;
- deterministic reporting of alias collisions; and
- unit and filesystem-backed regression coverage.
M4 does not create, delete, or replace links. M5 owns repair and must re-inspect an entry immediately before any mutation.
| Observed entry | Status | Existing target |
|---|---|---|
| No entry | Missing |
None |
| Regular file | Mismatch |
None |
| Reparse point that is not a symbolic link | Mismatch |
None |
| Symbolic link whose target is absent | Broken |
Decoded target |
| Symbolic link that resolves to the expected file | Ok |
Decoded target |
| Symbolic link that resolves to another existing file | Mismatch |
Decoded target |
This makes the M4 checklist and #46 authoritative: Broken is limited to an absent
symbolic-link target, while an existing different target is Mismatch. The conflicting
sentence in docs/PLAN.md is corrected during #48, and the decision is recorded during
#44 in the M4 ADR file.
Unreadable attributes, access denial, sharing violations, malformed reparse data, unexpected I/O failures, and an expected package executable that disappears during inspection are errors, not one of the four normal states. The error records the failed operation, path, and Win32 error code.
core/Model.h gains:
-
LinkEntryKind { None, RegularFile, SymbolicLink, OtherReparsePoint }; - the observed entry kind on
RepairItem; and -
AliasCollision, containing an alias and the distinct package executables that resolved to it.
core/LinkInspector.{h,cpp} provides:
-
LinkObservation, containing the entry kind, an optional decoded target, and the target relationNotApplicable,Missing,SameFile, orDifferentFile; -
classifyLink, a filesystem-independent classifier used directly by deterministic unit tests; -
inspectLink, the production Win32 adapter returning aRepairItem; - a boundary-checked symbolic-link reparse-buffer parser; and
-
detectAliasCollisions, which groups aliases with ordinal case-insensitive comparison and returns stable, sorted collision groups.
The production adapter:
- Uses
GetFileAttributesWto distinguish absence, a normal file, and a reparse point. Only clean file/path-not-found outcomes meanMissing. - Opens a reparse point with
CreateFileW,FILE_FLAG_OPEN_REPARSE_POINT, and read/write/delete sharing, held by a move-only RAII handle. - Reads at most the platform maximum reparse-data size with
DeviceIoControl(FSCTL_GET_REPARSE_POINT). - Accepts
IO_REPARSE_TAG_SYMLINKas a symbolic link, validates every offset and byte length before reading UTF-16 data, and resolves relative targets against the link's parent. Extended-length (\\?\) and UNC (\\?\UNC\) forms are normalized through the existingpaths::fromExtendedLengthPathhelper; the NT-namespace (\??\) prefix a symbolic link'sSubstituteNameuses is not covered by that helper and its decoding is new M4 code, not a reuse of it. Drive-relative forms need no normalization beyond this. - Opens the decoded target and expected executable for
FILE_READ_ATTRIBUTES, then comparesFILE_ID_INFOvolume serials and 128-bit file IDs. It does not silently fall back to lexical path comparison.
Every inspection path is read-only. An unknown reparse tag is preserved as
OtherReparsePoint and classified Mismatch, allowing M5 to avoid treating it as a
normal symbolic link.
-
#44 - Define link-inspection model and classification contract
- Add the model, observation, typed error, and pure classification function.
- Test every classification row and invalid observation combination.
- Add
docs/adr-phase-3.mdwith the status and re-inspection decisions. (ADR files are numbered sequentially by file, not by milestone:adr.mdis file 1,adr-phase-2.mdis file 2 and already holds the M3 decisions, so this is file 3 — not "adr-phase-4".) - Branch:
feature/44-link-inspection-contract.
-
#45 - Decode symbolic-link reparse targets
- Add the reparse handle/read path and boundary-checked parser.
- Test absolute, relative, UNC, and NT-path forms plus malformed buffers and wrong tags.
- Branch:
feature/45-reparse-target-decoder.
-
#46 - Probe link entries and compare target identity
- Complete the production
inspectLinkadapter. - Cover normal files, other reparse points, broken targets, matching and differing file identities, and an expected target that disappears.
- Branch:
feature/46-link-target-identity.
- Complete the production
-
#47 - Detect alias collisions deterministically
- Group aliases without case sensitivity, deduplicate the same executable path, and sort aliases and executable paths deterministically.
- Report collision groups separately so M6/M7 can warn and exclude them from automatic repair until the user chooses a target.
- Branch:
feature/47-alias-collisions.
-
#48 - Add regression matrix and finalize documentation
- Add cross-component regression coverage and update both project and filters files.
- Correct
docs/PLAN.md, complete M4 indocs/TODO.md, and record per-issue evidence indocs/task.md. "Maintain the ADR index" means appending a forward pointer todocs/adr-phase-3.mdat the end ofdocs/adr-phase-2.md, the same wayadr.mdalready points forward toadr-phase-2.md. - Branch:
feature/48-link-inspection-regression.
#45 is blocked by #44, #46 by #45, #47 by #44, and #48 by both #46 and #47. After #44 merges, #45 and #47 can proceed independently.
- Pure classification tests cover all four statuses, normal files, non-symlink reparse points, invalid observations, and the error boundary.
- Reparse parser tests cover absolute, relative, UNC, and NT namespace targets; offset and length bounds; odd UTF-16 byte lengths; truncated buffers; and unexpected tags.
- Filesystem-backed tests cover absent entries, normal files, valid and broken symbolic links, relative links, different targets, and file-identity comparison.
- Collision tests cover aliases that differ only by case, groups of two and three, distinct aliases, repeated copies of one executable, and stable output after input reordering.
- Each pull request builds core and tests at warnings-as-errors in
Debug|Releasetimesx64|ARM64. Debug and Release x64 tests must run throughvstest.console.exeand report the actual result. - ARM64 is reported as cross-built, not run, unless the tests execute on an ARM64 host.
- The executable project's known missing-entry-point linker failure remains expected until M6; M4 completion evidence uses the core and test projects.
- No third-party dependency is added.
- #44 through #48 are merged and closed, and #6 reflects their completion.
-
LinkInspectorand collision behavior have deterministic MSTest coverage. - All required x64 builds and tests pass; ARM64 builds pass with execution claims stated accurately.
-
docs/PLAN.md,docs/TODO.md, the M4 ADR,docs/task.md, and this Wiki page describe the same behavior. - No healthy
Oklink is modified, and no M4 code mutates the filesystem. - Canonical documentation and comments are English. Localized
*_ja.mdfiles are not read or changed.