Skip to content

fix(windows): preserve opaque ANSI shortcut path identity - #10547

Open
ShuaixinHuang wants to merge 1 commit into
morluto:mainfrom
ShuaixinHuang:replace/windows-shortcut-ansi-current-main
Open

ShuaixinHuang wants to merge 1 commit into
morluto:mainfrom
ShuaixinHuang:replace/windows-shortcut-ansi-current-main

Conversation

@ShuaixinHuang

@ShuaixinHuang ShuaixinHuang commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

Replacement history and current scope

Replaces #10181 at the author's explicit request, on current main fe80d52c9 (head 31cabe96d). This is the same defect and contribution, not a new independent finding. The original PR preserves its complete review/CI history. Only the focused four-file fix is carried forward.

Trigger, root cause, and fix

Shell Link ANSI fields are stored as byte-preserving Latin1 projections while the original Windows ANSI code page is unknown. Applying Unicode lowercasing or path normalization to these projections can invent equality or difference: CP932 bytes 83 41 and 83 61 name ア and ヂ, while 82 60 and 82 81 name A and a; 83 5c contains a trail byte that resembles a backslash.

The path-fact owner preserves opaque ANSI identity, certain identical-byte/ASCII-tail comparisons, and unknown full-path joins where multibyte boundaries are unobserved. A known suffix extension remains reportable; non-ASCII ANSI extension text stays unknown. Raw fields, exact artifact identity, Unicode controls and supported ASCII observations are retained through CLI/MCP. No host-code-page guess, new schema, provider, target execution or installation behavior is added.

Related work

Merged #9691 handles mixed ANSI/Unicode comparisons; this defect concerns opaque ANSI bytes even when both fields are ANSI. Open #10416 validates offsets and unreadable declarations, a different root; its patch does not establish multibyte character identity. Main's flag/unterminated-field handling is preserved.

Current-base verification

Linux x64, Node 24.22.1 / npm 11.16.0:

  • Final regression files with unchanged fe80d52c9 production: 10 failed / 62 passed. The production file was restored byte-for-byte in finally.
  • Fixed parser, application service, compiled CLI, filesystem and stdio MCP: 5 suites / 92 passed, including malformed/unknown inputs, Unicode counterparts, raw bytes and artifact digests.
  • Independent Python CP932 decoding confirms the five byte spellings above. Fixture bytes are parsed, not executed.
  • check:fast: 2 tasks passed (31.872 s). Three-file type-aware lint: 0 errors / 9 advisories. Formatting, diff review, normal commit/pre-push hooks and four post-hook input hashes passed.
  • An initial transplant duplicated an existing fixture import; that harness error was corrected before the final before/after runs and is not counted as a production failure.

No native Windows/IShellLink, real original code page, complete monolithic local check or new-PR hosted-CI pass is claimed. CI is pending. The final diff was reviewed for observation certainty, shared consumers and preserved unknowns.

Author readiness

Human owner: @ShuaixinHuang

Pending confirmation for this replacement's final diff and current-base evidence; the old PR's declaration is not reused.

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

Reapply the focused fix from morluto#10181 on current main at the author's request; preserves prior review history through the replacement PR.
@chatgpt-codex-connector

Copy link
Copy Markdown
Codex Review Summary: ❌ Failed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ❌ Failed 2026-11-14T14:08:03.926750Z
We couldn’t complete this review. Comment @codex review to try again.
31cabe9 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Repo admins can enable using credits for code reviews in their settings.

@greptile-apps

greptile-apps Bot commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The reviewed changes appear safe to merge.

Summary

The PR keeps opaque ANSI shortcut bytes out of Unicode path comparisons.

  • Icon and target paths stay unknown when opaque ANSI bytes cannot be compared.
  • LinkInfo keeps suffix extensions when opaque ANSI text prevents a full-path comparison.

ShuaixinHuang explicitly documents opaque ANSI joins remaining unknown as intentional; this was not treated as a regression.

The security review found no actionable issue in the changed code. Review conclusions are source-based; no tests were run during this review.

Replacement status could not be independently checked because GitHub authentication was unavailable. The supplied description says this PR replaces #10181 at ShuaixinHuang’s request.

Reviews (1) · Last reviewed commit: "fix(windows): preserve opaque ANSI short..." · Reviewed by Greptile

@XiaoBinGan

Copy link
Copy Markdown
Contributor

Reviewed 31cabe96 against upstream/main on Windows x64 / Node 24.22.1. I checked the direction of every behaviour change on a differential grid rather than reading the diff alone, because the risk in a change like this is a new claim, not a missing one.

The safety property holds. Across 30 target/icon pairs (ASCII, APP.EXE case variants, a trailing period, a truncated name, and an ANSI path carrying the CP932 lead byte 0x83 and trail byte 0x5C) the only changed verdicts are true → null. Nothing moved false → true or false → null, so the change never invents a difference and never invents equality — it only withdraws a claim it cannot support. That is the correct direction for an opaque-bytes reader.

Two things I confirmed rather than assumed:

  • Byte-identical ANSI paths still report false. C:\工具\app.exe against itself returns differs: false, so the left.path === right.path short-circuit is doing its job and the reader does not become uselessly silent on the common case.
  • target_extension is unaffected where the extension bytes are ASCII. A base path containing 0x83 still reports .exe, because the extension itself is ASCII and its bytes are unambiguous; only a non-ASCII extension goes to null. That matches what the PR body says.

Beyond the diff, the LinkInfo join behaves identically to main on a 30-cell base × suffix grid (C:\Tools\, C:\Tools, C:\Tools\app, C:\Tools\app.exe, C:\, C:\a.b\c × app.exe, "", .exe, app, x.txt) — zero divergent extension values. So the path: null branch is additive, as intended.

One observation, not a defect: recorded() computes extensionOf(path, ansi) for the icon path as well, but only targets[0].extension is ever read (observationsOf). If the icon's extension is not meant to be used, dropping that computation would keep RecordedPath's invariant narrower; if it is meant to be used, this is where it would go. Either way it is currently dead work on one path per link.

On CI: the only failing required check is Validate author declaration. That one failed for me too, on a push that changed nothing relevant, with a GraphQL 502 from openHeadsMatching (scripts/ci/pr-readiness.mjs:29) — this repo has ~890 open PRs and that query is heavy. If it stays red, re-running via an empty --amend commit clears it; it is not your diff.

This branch has not been deployed

No deployments
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