fix(repo): pin the Windows CLI shim to CRLF, the bytes it already ships

The parent commit pins the whole tree to LF and leaves one question open:
whether resources/win32/bin/orca.cmd should be an exception. Measured against
the shipped v1.4.192 installer, it should -- though for a narrower reason than
the open question assumed. See the finding below.

The release Windows runner pins nothing and sets no core.autocrlf, so it
converts this file on checkout. The committed blob is 644 bytes with 0 CR; the
copy inside orca-windows-setup.exe is 665 bytes with 21 CR, and is sha256-
identical to that blob with every LF doubled. 665 - 644 = 21, the file's line
count. The same installer carries its own control: the seven files under
resources/plugins/ were already pinned eol=lf and shipped unconverted, byte-
identical to their blobs. One artifact proves both that the conversion is live
and that a pin overrides it.

That reverses the framing. Pinning CRLF reproduces today's artifact exactly;
it is leaving the file to the blanket rule that flips a shipped byte. Verified
across every checkout config: with the pin the working tree is 665 B / 21 CR
under core.autocrlf=true, false and input alike; without it, 644 B / 0 CR under
all three, and a real windows-2022 runner confirms `i/lf w/crlf` with the pin
and `i/lf w/lf` without. The index blob is unchanged either way.

FINDING: LF is not broken. check-windows-launcher-line-endings.ps1 ran the shim
both ways on windows-2022 and both encodings passed both arms -- the goto-label
hazard did not reproduce. So this pin is a consistency choice, not a
correctness fix: it reproduces the bytes v1.4.192 shipped and stops them being
decided by a build-image default nobody controls. A uniform-LF decision would
now be defensible; what should survive either way is that something finally
executes this file. Until now nothing did -- smoke-packaged-cli.mjs resolves
the packaged CLI to resources/bin/orca.exe on win32, never the .cmd beside it.

The shim sits outside the parent gate's population (no shebang, mode 100644),
so the two rules cannot contradict each other; a test asserts that.

check-line-ending-policy.mjs now asserts the exception rather than merely
permitting it: the blob must still be LF and eol must resolve to crlf. An
exception nothing checks is how this file's encoding became a runner-image
accident in the first place.

check-windows-launcher-line-endings.ps1 adds the empirical half on the existing
package (windows) job, first step after checkout, one second. It confirms the
runner really wrote CRLF, then runs the shim's guard path and its fall-through
under a real cmd.exe, using a copy of cmd.exe as the orca.exe the shim requires
-- without one the shim exits 1 before reaching either arm. It reports the LF
verdict without ever failing the job on it. Both checks were watched failing
with the pin removed before this landed.
This commit is contained in:
Merge Sim
2026-08-29 19:40:04 -07:00
parent 9cf6ba9cd7
commit 82de6adbbf
7 changed files with 366 additions and 19 deletions
+10 -6
View File
@@ -23,9 +23,13 @@
# wrapper does change.
/src/main/__fixtures__/shell-wrapper-snapshots/*.txt linguist-generated=true
# Undecided (STA-4307 follow-up): whether the Windows CLI shim should ship CRLF.
# cmd.exe reads LF-only batch files, but orca.cmd uses `goto` with labels, which is
# the one batch construct with a history of misbehaving without CRLF. Pinning it
# changes a shipped byte, so it is a product call rather than a hygiene fix.
# If we decide yes, it is exactly this one line:
# /resources/win32/bin/orca.cmd text eol=crlf
# The one deliberate CRLF exception, and it is not a hygiene lapse: it is the byte
# that already ships. The release runner has no pin and sets no core.autocrlf, so it
# converted this file on checkout — the committed blob is 644 B with 0 CR, while the
# copy inside v1.4.192's orca-windows-setup.exe is 665 B with 21 CR, the same bytes
# with every LF doubled. Under the blanket rule above it would check out LF instead,
# changing a shipped byte on a batch file that uses `goto` with labels, in a shape
# nothing in CI executes. The pin only changes the working tree; the index stays LF.
# check-line-ending-policy.mjs asserts this pin still resolves, and
# check-windows-launcher-line-endings.ps1 runs the shim both ways on windows-2022.
/resources/win32/bin/orca.cmd text eol=crlf
+7
View File
@@ -661,6 +661,13 @@ jobs:
with:
persist-credentials: false
# Why here and not after packaging: this needs nothing but the checkout, runs in
# seconds, and the encoding it asserts is decided by the checkout itself. Placed
# first it reports in about a minute instead of after a 20-minute package.
- name: Check Windows CLI shim line endings
shell: pwsh
run: ./config/scripts/check-windows-launcher-line-endings.ps1
- name: Cache electron-builder downloads
uses: actions/cache@v5
with:
+1 -1
View File
@@ -46,7 +46,7 @@ Orca targets macOS, Linux, and Windows. Keep all platform-dependent behavior beh
- **Keyboard shortcuts**: Never hardcode `e.metaKey`. Use a platform check (`navigator.userAgent.includes('Mac')`) to pick `metaKey` on Mac and `ctrlKey` on Linux/Windows. Electron menu accelerators should use `CmdOrCtrl`.
- **Shortcut labels in UI**: Display `⌘` / `⇧` on Mac and `Ctrl+` / `Shift+` on other platforms.
- **File paths**: Use `path.join` or Electron/Node path utilities — never assume `/` or `\`.
- **Line endings**: the whole tree is LF (`* text=auto eol=lf`), so a `core.autocrlf=true` Windows clone no longer breaks shipped shell scripts or byte-compared artifacts. `pnpm lint` fails if any tracked file with a shebang or the executable bit acquires CRLF. See [`docs/reference/line-endings.md`](./docs/reference/line-endings.md).
- **Line endings**: the tree is LF (`* text=auto eol=lf`), so a `core.autocrlf=true` Windows clone no longer breaks shipped shell scripts or byte-compared artifacts. The one exception is `resources/win32/bin/orca.cmd`, pinned `eol=crlf` because that is the byte the Windows installer already ships. `pnpm lint` fails if any tracked file with a shebang or the executable bit acquires CRLF, or if that pin stops resolving. See [`docs/reference/line-endings.md`](./docs/reference/line-endings.md).
- **Windows setup scripts**: the setup/issue-command runner is a `.cmd` batch file unless the script starts with a `#!` line — never derive that from the user's terminal-shell preference, and never launch a `.cmd` runner with a bare `cmd.exe /c` from a Git Bash pane (MSYS rewrites the `/c`). See [`docs/reference/windows-setup-shell.md`](./docs/reference/windows-setup-shell.md).
- **Windows child processes**: start them through `runProcess`/`spawnProcess` in `src/shared/child-process/` — never `child_process` directly. It pins `windowsHide`, refuses `shell: true`, and encodes `.cmd`/`.bat` arguments so neither `CommandLineToArgvW` nor `cmd.exe` mangles them. A ratchet test fails on any new direct import.
- **Windows process enumeration**: read the table through `src/main/windows/windows-process-table.ts`, never by forking `powershell.exe`. See [`docs/reference/windows-process-enumeration.md`](./docs/reference/windows-process-enumeration.md).
+89 -11
View File
@@ -85,6 +85,57 @@ export function findViolations(entries, attrs) {
return violations
}
/**
* The one path the blanket LF rule must NOT reclaim. `cmd.exe` is its only consumer:
* it carries no shebang and no executable bit, so the population above never sees it.
* v1.4.192 already shipped it converted — a 644-byte LF blob arrived in the installer
* as 665 bytes with 21 CR, because the release runner pins nothing and sets no
* `core.autocrlf`. Pinning reproduces those bytes on every machine; leaving it to the
* blanket rule would change them. Asserted, not just allowed: an exception nothing
* checks is exactly how this file's encoding became a runner-image accident.
*/
export const CRLF_PINNED_PATHS = ['resources/win32/bin/orca.cmd']
/**
* Mirror of {@link findViolations} for the CRLF exception, and deliberately not a
* `!== 'lf'` inversion of it: the blob assertion points the same way for both rules.
* `eol=crlf` converts on checkout, so the stored blob must still be LF — a CRLF blob
* would reach macOS and Linux too, where the exception has no reason to exist.
*
* @param pins {{ path: string, tracked: boolean, crlf: boolean }[]}
* @param attrs Map<string, { eol?: string }>
*/
export function findPinViolations(pins, attrs) {
const violations = []
for (const pin of pins) {
// A pin naming a path that no longer exists is a silent hole, not a clean repo.
if (!pin.tracked) {
violations.push({
path: pin.path,
rule: 'pinned to CRLF but not tracked',
detail: 'the pin no longer names a file; move the .gitattributes line or drop it'
})
continue
}
if (pin.crlf) {
violations.push({
path: pin.path,
rule: 'committed blob contains CRLF',
detail: 'eol=crlf already converts on checkout; a CRLF blob ships CRLF everywhere'
})
}
const eol = attrs.get(pin.path)?.eol
if (eol !== 'crlf') {
violations.push({
path: pin.path,
rule: `checked out with eol=${eol ?? 'unspecified'}, not crlf`,
detail: 'this is the shipped Windows CLI shim; LF here changes a byte cmd.exe parses'
})
}
}
return violations
}
/**
* Why the explicit status check: `git grep` exits 1 on "no matches", which is
* indistinguishable from a failure unless we look. A silently empty result here would
@@ -148,7 +199,7 @@ function readIndexBlobs(root, files) {
* working-tree scan would fail for everyone on Windows and prove nothing. The committed
* blob is the thing that actually ships.
*/
export function collectExecutableArtifacts(root) {
function readIndex(root) {
const index = new Map()
const executableBit = new Set()
for (const entry of gitText(root, ['ls-files', '-s', '-z']).split('\0')) {
@@ -166,6 +217,11 @@ export function collectExecutableArtifacts(root) {
executableBit.add(file)
}
}
return { index, executableBit }
}
export function collectExecutableArtifacts(root) {
const { index, executableBit } = readIndex(root)
// `git grep` narrows 20k tracked files to ~130 candidates in one C-side pass; the
// anchor is per-line, so this is a superset that the blob read below trims exactly.
// `-z` because plain `-l` quotes any path git considers unusual.
@@ -206,23 +262,42 @@ export function collectExecutableArtifacts(root) {
return entries
}
export function collectCrlfPins(root) {
const { index } = readIndex(root)
const tracked = CRLF_PINNED_PATHS.filter((file) => index.has(file))
const blobs = readIndexBlobs(
root,
tracked.map((file) => ({ path: file, sha: index.get(file) }))
)
return CRLF_PINNED_PATHS.map((file) => ({
path: file,
tracked: index.has(file),
crlf: containsCrlf(blobs.get(file) ?? Buffer.alloc(0))
}))
}
export function checkLineEndingPolicy(root) {
const entries = collectExecutableArtifacts(root)
const pins = collectCrlfPins(root)
const queried = [...entries.map((e) => e.path), ...pins.map((p) => p.path)]
const attrs = parseCheckAttr(
gitText(
root,
['check-attr', '-z', '--stdin', 'eol'],
`${entries.map((e) => e.path).join('\0')}\0`
)
gitText(root, ['check-attr', '-z', '--stdin', 'eol'], `${queried.join('\0')}\0`)
)
return { entries, violations: findViolations(entries, attrs) }
return {
entries,
pins,
violations: [...findViolations(entries, attrs), ...findPinViolations(pins, attrs)]
}
}
function main(root) {
const { entries, violations } = checkLineEndingPolicy(root)
if (entries.length === 0) {
const { entries, pins, violations } = checkLineEndingPolicy(root)
if (entries.length === 0 || pins.length === 0) {
// A population of zero means the discovery broke, not that the repo got clean.
console.error('::error::line-ending policy: found no executable artifacts to check.')
console.error(
'::error::line-ending policy: nothing to check' +
` (${entries.length} executable artifact(s), ${pins.length} CRLF-pinned path(s)).`
)
return 1
}
if (violations.length > 0) {
@@ -236,7 +311,10 @@ function main(root) {
)
return 1
}
console.log(`line-ending policy OK — ${entries.length} executable artifact(s), all LF.`)
console.log(
`line-ending policy OK — ${entries.length} executable artifact(s) LF, ` +
`${pins.length} CRLF-pinned path(s) still pinned.`
)
return 0
}
@@ -5,6 +5,8 @@ import { describe, expect, it } from 'vitest'
import {
checkLineEndingPolicy,
containsCrlf,
CRLF_PINNED_PATHS,
findPinViolations,
findViolations,
hasShebang,
parseCheckAttr
@@ -86,6 +88,43 @@ describe('findViolations', () => {
})
})
describe('findPinViolations', () => {
const shim = 'resources/win32/bin/orca.cmd'
const pin = (over = {}) => [{ path: shim, tracked: true, crlf: false, ...over }]
const attrs = (eol) => new Map([[shim, eol === undefined ? {} : { eol }]])
it('passes the shim when it is LF in the index and CRLF on checkout', () => {
expect(findPinViolations(pin(), attrs('crlf'))).toEqual([])
})
// This is what #17303's blanket rule alone produces, and what flips a shipped byte.
it('flags the shim once the blanket LF rule reclaims it', () => {
const found = findPinViolations(pin(), attrs('lf'))
expect(found).toHaveLength(1)
expect(found[0].rule).toContain('not crlf')
})
it('flags an unpinned shim, which is the accident that decided its bytes before', () => {
const found = findPinViolations(pin(), attrs(undefined))
expect(found).toHaveLength(1)
expect(found[0].rule).toContain('unspecified')
})
// eol=crlf converts on checkout, so a CRLF blob would reach macOS and Linux too.
it('still requires the stored blob to be LF', () => {
const found = findPinViolations(pin({ crlf: true }), attrs('crlf'))
expect(found).toHaveLength(1)
expect(found[0].rule).toContain('blob contains CRLF')
})
// Otherwise deleting the file would silently empty the rule instead of failing it.
it('flags a pin whose path is no longer tracked instead of skipping it', () => {
const found = findPinViolations(pin({ tracked: false }), attrs('crlf'))
expect(found).toHaveLength(1)
expect(found[0].rule).toContain('not tracked')
})
})
describe('the repo itself', () => {
const root = new URL('../..', import.meta.url).pathname
@@ -101,6 +140,18 @@ describe('the repo itself', () => {
expect(() => checkLineEndingPolicy(tmpdir())).toThrow(/git ls-files exited/)
})
it('keeps the Windows CLI shim pinned to CRLF, outside the LF population', () => {
const { entries, pins } = checkLineEndingPolicy(root)
// The literal path, not CRLF_PINNED_PATHS: comparing the result to the constant
// that produced it passes just as happily when someone empties the constant.
expect(CRLF_PINNED_PATHS).toContain('resources/win32/bin/orca.cmd')
expect(pins.map((p) => p.path)).toContain('resources/win32/bin/orca.cmd')
expect(pins.every((p) => p.tracked && !p.crlf)).toBe(true)
// The two rules must not overlap: the shim has no shebang and no executable bit,
// so requiring CRLF here cannot contradict the LF rule above.
expect(entries.map((e) => e.path)).not.toContain('resources/win32/bin/orca.cmd')
})
it('covers the shipped launchers, the packaging scripts and the commit hook', () => {
const covered = new Set(checkLineEndingPolicy(root).entries.map((e) => e.path))
for (const shipped of [
@@ -0,0 +1,157 @@
# Behavioural check for the shipped Windows CLI shim, resources/win32/bin/orca.cmd.
#
# The static half lives in check-line-ending-policy.mjs, which asserts the pin still
# resolves. This half asserts the thing the pin exists for: that the shim, in exactly
# the encoding CI checked out, still runs under a real cmd.exe. Nothing else executes
# it — smoke-packaged-cli.mjs resolves the packaged CLI to resources/bin/orca.exe on
# win32, so the .cmd beside it has never been run by any test.
#
# It also measures the LF encoding and reports the outcome. That result is a finding,
# not a policy violation, so it never fails the job on its own: the pin means LF is
# not what ships either way, and a green run must not be read as "LF was fine".
#
# Contract: this script fails or reports explicitly. There is no path on which a
# missing fixture, a broken stub, or an unexecuted arm reads as a pass.
#
# Why PowerShell and not a config/scripts/*.mjs: driving a .cmd file from Node would
# mean child_process with the .cmd argument-encoding hazard AGENTS.md forbids, and the
# repo's runProcess wrapper is TypeScript under src/. A pwsh step also has no MSYS
# layer, so the bare `/c` switch survives (docs/reference/windows-setup-shell.md).
$ErrorActionPreference = 'Stop'
# Every native call below merges its own streams inside cmd, so PowerShell should never
# see stderr — but on hosts where this preference is on, one stray line would abort the
# run mid-arm and read as a harness crash rather than a result.
if (Test-Path variable:PSNativeCommandUseErrorActionPreference) {
$PSNativeCommandUseErrorActionPreference = $false
}
$RepoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
$Relative = 'resources/win32/bin/orca.cmd'
$Source = Join-Path $RepoRoot 'resources\win32\bin\orca.cmd'
function Fail($message) {
Write-Host "::error::$message"
exit 1
}
function Get-CrCount([byte[]]$bytes) {
$count = 0
foreach ($byte in $bytes) {
if ($byte -eq 13) { $count++ }
}
return $count
}
# `/s /c` so cmd strips exactly the outer quotes and takes the rest literally, instead
# of applying its conditional quote-stripping rule. The `2>&1` is inside the command
# line so cmd merges the streams itself; letting PowerShell merge a native command's
# stderr turns it into an ErrorRecord under some hosts and aborts the run.
function Invoke-Shim([string]$directory, [string]$arguments) {
Push-Location $directory
try {
$output = & cmd.exe /s /c "orca.cmd $arguments 2>&1"
return [pscustomobject]@{ Code = $LASTEXITCODE; Output = ($output -join "`n") }
} finally {
Pop-Location
}
}
if (-not (Test-Path $Source)) {
Fail "PROBE CANNOT RUN: $Relative is absent. Not a pass."
}
# --- What the checkout produced ------------------------------------------------
# Read through git rather than off disk: `i/` is the stored blob and `w/` is the
# working tree, which is the whole distinction the pin turns on.
$eol = (& git -C $RepoRoot ls-files --eol -- $Relative) -join ''
if ($LASTEXITCODE -ne 0 -or -not $eol) {
Fail "PROBE CANNOT RUN: git ls-files --eol returned nothing for $Relative. Not a pass."
}
Write-Host "checkout state: $eol"
if ($eol -notmatch '(^|\s)i/lf(\s|$)') {
Fail "The committed blob is not LF ($eol). eol=crlf converts on checkout, so a CRLF blob ships CRLF on macOS and Linux too."
}
if ($eol -notmatch '(^|\s)w/crlf(\s|$)') {
Fail "This runner checked $Relative out as LF, not CRLF ($eol). v1.4.192 shipped it as CRLF; the /resources/win32/bin/orca.cmd text eol=crlf pin is missing or was overridden."
}
$shipped = [IO.File]::ReadAllBytes($Source)
Write-Host ("as checked out: {0} bytes, {1} CR" -f $shipped.Length, (Get-CrCount $shipped))
# --- The stub the shim shells out to -------------------------------------------
# orca.cmd exits 1 at its `if not exist "%LAUNCHER%"` block before reaching any of
# the code under test, so a real orca.exe has to be beside it or both arms measure
# nothing. A copy of cmd.exe is the stub: `orca.cmd /c exit 7` falls through to
# `"%LAUNCHER%" /c exit 7`, making 7 a sentinel that only a shim which parsed to its
# last line can produce. No compiler, and nothing to skip on.
if (-not $env:ComSpec) {
Fail 'PROBE CANNOT RUN: %ComSpec% is unset, so there is no cmd.exe to test against. Not a pass.'
}
$scratch = if ($env:RUNNER_TEMP) { $env:RUNNER_TEMP } else { $env:TEMP }
if (-not $scratch) {
Fail 'PROBE CANNOT RUN: neither RUNNER_TEMP nor TEMP is set. Not a pass.'
}
$work = Join-Path $scratch 'orca-shim-eol'
Remove-Item $work -Recurse -Force -ErrorAction SilentlyContinue
New-Item -ItemType Directory -Force -Path $work | Out-Null
$stub = Join-Path $work 'orca.exe'
Copy-Item $env:ComSpec $stub -Force
# Validate the sentinel itself, or a stub that cannot return 7 would read as a shim
# that failed to parse.
& $stub /s /c "exit 7" | Out-Null
if ($LASTEXITCODE -ne 7) {
Fail "PROBE CANNOT RUN: the stub launcher returned $LASTEXITCODE, not the 7 sentinel. Not a pass."
}
$arms = @{}
foreach ($encoding in @('shipped', 'lf')) {
$directory = Join-Path $work $encoding
New-Item -ItemType Directory -Force -Path $directory | Out-Null
Copy-Item $stub $directory -Force
# The `shipped` arm is the checked-out bytes verbatim — no rewrite, so it measures
# what this commit actually produces. The `lf` arm strips the CRs from those same
# bytes, which is what the blanket rule alone would have written.
$target = Join-Path $directory 'orca.cmd'
if ($encoding -eq 'shipped') {
[IO.File]::WriteAllBytes($target, $shipped)
} else {
[IO.File]::WriteAllBytes($target, [byte[]]($shipped | Where-Object { $_ -ne 13 }))
}
$written = [IO.File]::ReadAllBytes($target)
Write-Host ("[{0}] {1} bytes, {2} CR" -f $encoding, $written.Length, (Get-CrCount $written))
# Arm 1, the guard path: `goto :unsafe_body` must fire. Label seeking is the one
# batch construct with a history of misbehaving without CRLF, so this is the arm
# the encoding question actually rides on.
$guard = Invoke-Shim $directory 'orchestration send --body x'
# Arm 2, the fall-through: no goto taken, the shim reaches `"%LAUNCHER%" %*` and
# propagates its status. Proves the file parses end to end.
$through = Invoke-Shim $directory '/c exit 7'
$guardOk = ($guard.Code -eq 2) -and
($guard.Output -match 'cannot safely forward orchestration message bodies')
$throughOk = $through.Code -eq 7
Write-Host ("[{0}] guard exit={1} want 2, message {2} | fall-through exit={3} want 7" -f
$encoding, $guard.Code, $(if ($guardOk) { 'present' } else { 'MISSING' }), $through.Code)
Write-Host ("[{0}] guard output: {1}" -f $encoding, $guard.Output)
$arms[$encoding] = [pscustomobject]@{ GuardOk = $guardOk; ThroughOk = $throughOk }
}
# --- Verdict -------------------------------------------------------------------
if (-not ($arms['shipped'].GuardOk -and $arms['shipped'].ThroughOk)) {
Fail "The Windows CLI shim does not behave in the encoding this commit ships. Guard path must exit 2 with its own message and the fall-through must propagate 7."
}
if ($arms['lf'].GuardOk -and $arms['lf'].ThroughOk) {
Write-Host 'FINDING: LF is safe for this shim on this image — both arms matched the shipped encoding. The CRLF pin is a consistency choice (it reproduces the shipped bytes), not a correctness one.'
} else {
Write-Host 'FINDING: LF breaks this shim on this image, while the shipped encoding passes both arms. The CRLF pin is load-bearing.'
}
Write-Host 'Windows CLI shim OK in the encoding this commit ships.'
exit 0
+51 -1
View File
@@ -9,7 +9,8 @@
```
That means Git stores every text file with LF **and** writes it to disk with LF, on
macOS, Linux and Windows alike. Nothing in the tree is checked out with CRLF.
macOS, Linux and Windows alike. One file is deliberately checked out with CRLF —
the Windows CLI shim, [below](#the-one-crlf-exception). Everything else is LF.
## What this fixes
@@ -47,6 +48,48 @@ git reset --hard
A fresh clone needs nothing.
## The one CRLF exception
`resources/win32/bin/orca.cmd` is pinned the other way:
```
/resources/win32/bin/orca.cmd text eol=crlf
```
This is not a hygiene lapse. It is the byte that already ships. The release Windows
runner pins nothing and sets no `core.autocrlf`, so it converted this file on checkout
long before the blanket rule existed:
| | committed blob | inside v1.4.192's `orca-windows-setup.exe` |
| --- | ---: | ---: |
| size | 644 B | 665 B |
| CR | 0 | 21 |
665 − 644 = 21, exactly the file's line count — the same bytes with every `\n` doubled.
The same installer carries its own control: the seven files under `resources/plugins/`
were already pinned to LF and shipped unconverted, byte-identical to their blobs. One
artifact, both directions.
So the pin **reproduces** today's shipped launcher rather than changing it, and makes it
deterministic instead of dependent on a runner-image default nobody controls. Leaving
the file to the blanket rule is what would flip a shipped byte — on a batch file that
uses `goto` with labels, into an encoding no smoke test executes
(`config/scripts/smoke-packaged-cli.mjs` resolves the packaged CLI to
`resources/bin/orca.exe` on win32, never the `.cmd` beside it).
Whether LF actually *breaks* `cmd.exe` here is measured, not assumed —
`config/scripts/check-windows-launcher-line-endings.ps1` runs the shim both ways on
`windows-2022` and reports which. The pin stands either way, because reproducing the
shipped bytes is the point.
The pin changes the working tree only. The stored blob stays LF, so
`git add --renormalize` still stages nothing:
```
$ git ls-files --eol -- resources/win32/bin/orca.cmd
i/lf w/crlf attr/text eol=crlf resources/win32/bin/orca.cmd
```
## The three exemptions
- `config/patches/*.patch` are `-text`: pnpm hashes each patch byte-for-byte, so any
@@ -68,6 +111,13 @@ executable bit, 132 files today — it asserts both halves independently:
Neither implies the other: a clean blob still breaks Windows under a stray `eol=crlf`,
and a correct attribute still ships CRLF if the blob itself carries it.
The same script asserts the CRLF exception with the mirrored rules — the blob must
still be LF, and `eol` must resolve to `crlf` — so the exception cannot be silently
reclaimed by the blanket rule or quietly widened. On Windows,
`check-windows-launcher-line-endings.ps1` adds the empirical half: it reads
`git ls-files --eol` to confirm the runner really wrote CRLF, then executes the shim's
guard path and its fall-through under a real `cmd.exe`.
The population is derived from file content, not hand-listed. A curated list would have
had to name 122 files on the day this landed and would silently miss the 123rd — which
is exactly how the broken launcher shipped. Equally, the gate is not a repo-wide "no