From 78b51020d07090219109d2ec808c4bf89e3e4e56 Mon Sep 17 00:00:00 2001 From: brady gaster Date: Fri, 21 Aug 2026 02:48:46 -0700 Subject: [PATCH 1/2] docs(e2e): make the renormalize selector attribute-driven, not glob-shaped (#1809) The remediation shipped in #1808 selected `*.mjs`. That was correct against the known-affected set and wrong against the PINNED set, and Booster's #1809 measurement invalidated it about forty minutes after it merged. Measured on dev: 173 files carry `attr/text eol=lf`, and 26 of them were CRLF on disk. Only ~6 were .mjs. The old sweep would have repaired 6 of 26 and left 20 broken - including cli.js, index.cjs, lib/rework.cjs and every .ps1. That is worse than shipping no remediation at all. A partial repair produces a third distinct failure set matching neither the doc nor the card, which reads as "the remediation is unreliable" rather than "the selector was wrong" - it discredits the correct procedure. Same shape as converting a visibly-dirty file into an invisibly-broken one. The fix is to enumerate by attribute rather than by extension, so the command tracks .gitattributes automatically and cannot drift the next time a non-matching extension gets pinned. Verified end to end by executing the documented block verbatim: 26 -> 0 pinned files CRLF, tree clean, idempotent on a second run, and the two EOL suites go 13-failed / 0-tests to 26 passed. Structure follows the split we agreed: the doc keeps the WHY (the blind-oracle table, `--force` does not force, `git status` structurally cannot see it, the 26-test proof) because that argument is what makes someone actually run the step rather than skip it. The HOW is now stated exactly once here, and the card and runbook point at it instead of restating it - so selector drift between two copies becomes structurally impossible rather than merely fixed. Step 2 restores a scoped file list rather than `git checkout -- .`, which is prohibited by repo convention after it silently reverted in-progress work earlier tonight. Refs #1809, #1793. Supersedes the remediation block from #1808. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 70370e36-33b0-4786-bd72-4cf15518daa6 --- .squad/e2e/windows-test-baseline.md | 57 +++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/.squad/e2e/windows-test-baseline.md b/.squad/e2e/windows-test-baseline.md index 4a776ce36..96dc5c4d0 100644 --- a/.squad/e2e/windows-test-baseline.md +++ b/.squad/e2e/windows-test-baseline.md @@ -155,17 +155,60 @@ git ls-files --eol scripts/check-changeset-drift.mjs 26 tests recovered by changing only the bytes on disk. `w/crlf` → `w/lf`. -**Remediation (do this before the first run tomorrow):** +**Remediation — the single authoritative procedure. Run before the first test run.** + +> ⚠️ **This block is the one source of truth for the renormalize.** The card and the +> runbook point *here*; they must not restate the command. An earlier revision of this +> doc carried a `*.mjs`-scoped version that drifted out of date **within 40 minutes** +> of merging — see "why not a glob" below. ```powershell -git ls-files -- '*.mjs' | % { Remove-Item -LiteralPath $_ -Force } -git checkout -- . -git ls-files --eol scripts/check-changeset-drift.mjs # must show w/lf +# 1. Enumerate PINNED files by attribute — never by glob. +$pinned = git ls-files --eol | + Where-Object { $_ -match 'attr/text eol=lf' } | + ForEach-Object { ($_ -split "`t")[-1].Trim() } + +# 2. Delete, then restore scoped to that list. Sweep ALL of them, unconditionally. +$pinned | ForEach-Object { Remove-Item -LiteralPath $_ -Force -ErrorAction SilentlyContinue } +$pinned | ForEach-Object { git checkout -- $_ } + +# 3. Assert the artifact. This must print 0. +(git ls-files --eol | + Where-Object { $_ -match 'attr/text eol=lf' } | + Where-Object { $_ -match '^i/lf\s+w/crlf' }).Count ``` -A plain `git checkout --` on an unmodified file is a **no-op** and does nothing; the -file must be deleted first to force re-checkout. This is the single most likely -source of a false alarm tomorrow morning. +**Measured on `dev`:** 173 files pinned, **26** of them CRLF on disk before → **0** +after, tree clean, and the two EOL suites go `13 failed`/`0 tests` → **26 passed**. + +**Sweep all 173 unconditionally — do not try to repair only the broken ones.** Every +oracle that could tell you *which* files are affected is blind (see the table below), +so selection is guesswork. The sweep is idempotent and costs seconds. + +**Why not a glob.** The `*.mjs` selector was correct against the *known-affected* set +and wrong against the *pinned* set. Of the 26 files actually needing repair, **only +about 6 were `.mjs`** — it would have missed **20**, including `cli.js`, `index.cjs`, +`lib/rework.cjs`, and every `.ps1`. Any glob-shaped selector regenerates this bug the +next time a non-matching extension gets pinned; the attribute-driven enumeration +tracks `.gitattributes` automatically and cannot drift. + +This is why a partial repair is **worse than none**: it produces a third distinct +failure set that matches neither the doc nor the card, which reads as *"the +remediation is unreliable"* rather than *"the selector was wrong."* + +### Every oracle here is blind — this is why the sweep is unconditional + +| Thing you would naturally reach for | What it does | +| --- | --- | +| `git checkout -- ` on an unmodified file | **Silent no-op.** Reports success, changes nothing | +| `git checkout --force -- ` | **Also silently fails**, then reports clean | +| `git status` | **Structurally cannot see it.** Once the blob is LF, a CRLF worktree is invisible | +| A green test suite | #1788 loaded **0 tests** and looked like a pass | +| `git ls-files --eol` | ✅ **The one working oracle.** Use it | + +The file must be **deleted first** to force re-checkout. This is the single most +likely source of a false alarm tomorrow morning — and note that `git checkout -- .` +is prohibited by repo convention, which is why step 2 restores a scoped list. ### Post-renormalize numbers on merged `dev` From 84dc819142ec2684cbfd1073d039386146eb33a8 Mon Sep 17 00:00:00 2001 From: brady gaster Date: Fri, 21 Aug 2026 02:55:21 -0700 Subject: [PATCH 2/2] docs(e2e): post-condition must match w/mixed, not just w/crlf The 26-vs-34 discrepancy against Booster's measurement resolves as benign, but finding out why exposed a real defect in the block I shipped an hour ago. Resolution of the gap: no pinned file on this tree is currently w/mixed, and docs/src/pages/rss.xml.js - his specific example - is already w/lf here. It was repaired by the sweep because step 1 enumerates by attribute and sweeps all 173 unconditionally rather than selecting what looks broken. So the repair was complete; only the COUNT was short. My "26" was measured with a w/crlf-only filter, which silently excludes w/mixed. His 34 used w/(crlf|mixed). Part of the 8-file gap is that counting artifact, part is genuine worktree difference. The defect: step 3, the post-condition, used the same w/crlf-only filter. A pinned file with a single stray CRLF line is reported w/mixed, not w/crlf, so the assertion could print 0 while a pinned file remained broken. That is a blind oracle sitting in the safety net of a procedure whose entire subject is blind oracles. Fixed by matching w/(crlf|mixed), with a comment saying the alternation is not a simplification target so nobody collapses it later. Verified with a discriminating control rather than by reading: i/lf w/mixed + pinned, crlf-only filter -> False (missed it, silently) i/lf w/mixed + pinned, crlf|mixed filter -> True (caught) i/lf w/lf + pinned, crlf|mixed filter -> False (no over-match) The doc's fenced block was then re-executed verbatim and still returns 0. The 26 figure is retained but explicitly labelled an undercount, with the reason, because the honest version of that number is the argument for why the sweep is unconditional: the count is an unreliable oracle, so it is never used to choose what to repair - only to confirm the result. Added the w/crlf-only filter to the blind-oracle table as its own row. Refs #1809, #1793. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 70370e36-33b0-4786-bd72-4cf15518daa6 --- .squad/e2e/windows-test-baseline.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/.squad/e2e/windows-test-baseline.md b/.squad/e2e/windows-test-baseline.md index 96dc5c4d0..23ec666ee 100644 --- a/.squad/e2e/windows-test-baseline.md +++ b/.squad/e2e/windows-test-baseline.md @@ -173,14 +173,26 @@ $pinned | ForEach-Object { Remove-Item -LiteralPath $_ -Force -ErrorAction Silen $pinned | ForEach-Object { git checkout -- $_ } # 3. Assert the artifact. This must print 0. +# `w/mixed` is NOT a simplification target: a file with a single CRLF line +# (e.g. docs/src/pages/rss.xml.js) is reported `w/mixed`, not `w/crlf`, and a +# crlf-only filter drops it silently while reporting completeness. (git ls-files --eol | Where-Object { $_ -match 'attr/text eol=lf' } | - Where-Object { $_ -match '^i/lf\s+w/crlf' }).Count + Where-Object { $_ -match '^i/lf\s+w/(crlf|mixed)' }).Count ``` **Measured on `dev`:** 173 files pinned, **26** of them CRLF on disk before → **0** after, tree clean, and the two EOL suites go `13 failed`/`0 tests` → **26 passed**. +> **Caveat on that 26.** It was counted with a `w/crlf`-only filter and therefore +> **undercounts** — it silently excludes files git reports as `w/mixed`. Booster +> measured **34** on his tree using `w/(crlf|mixed)`. Part of that 8-file gap is this +> counting artifact, part is genuine worktree difference. **The repair is unaffected +> either way**, because step 1 enumerates by attribute and sweeps all 173 +> unconditionally rather than selecting the ones that look broken. This is precisely +> why the sweep is unconditional: the count is an unreliable oracle, so it is never +> used to choose what to repair — only to confirm the result. + **Sweep all 173 unconditionally — do not try to repair only the broken ones.** Every oracle that could tell you *which* files are affected is blind (see the table below), so selection is guesswork. The sweep is idempotent and costs seconds. @@ -204,7 +216,8 @@ remediation is unreliable"* rather than *"the selector was wrong."* | `git checkout --force -- ` | **Also silently fails**, then reports clean | | `git status` | **Structurally cannot see it.** Once the blob is LF, a CRLF worktree is invisible | | A green test suite | #1788 loaded **0 tests** and looked like a pass | -| `git ls-files --eol` | ✅ **The one working oracle.** Use it | +| A `w/crlf`-only filter | **Silently drops `w/mixed`** — one CRLF line in an otherwise-LF file. Never simplify the alternation | +| `git ls-files --eol` | ✅ **The one working oracle.** Use it — with `w/(crlf\|mixed)` | The file must be **deleted first** to force re-checkout. This is the single most likely source of a false alarm tomorrow morning — and note that `git checkout -- .`