From e7970f54cca5a49a3e6745f25502e796145d6739 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Aug 2026 02:22:40 +0000 Subject: [PATCH 1/7] Add line-ending self-check to run_setup.bat; file findings from a real Windows Sandbox debugging session A raw download of run_setup.bat (GitHub's Raw button, raw.githubusercontent.com) serves the file with LF-only line endings instead of the CRLF a real git checkout produces (.gitattributes' text=auto eol=lf normalizes the stored blob to LF; the *.bat eol=crlf override only affects checkout, never what GitHub serves raw). cmd.exe's goto/call label-seeking silently misbehaves on the result, producing a confusing partial run with no clear error -- confirmed as the root cause of a real debugging session (multiple pauses, a PyInstaller build loop with no environment behind it, some files written and others not). - run_setup.bat: new self-check as literally the first thing the script does (before any other goto/call, so it stays reliable even on a corrupted copy), failing fast with a clear, actionable message instead of a silent partial run. - README.md: TL;DR bullet recommending git clone over a raw download. - docs/open-questions.md: pro/con on fixing the distribution channel itself (gitattributes options vs. a verified-CRLF release asset), left for the maintainer to decide. - CLAUDE.md: Active Backlog Items 44-52 -- the line-ending finding plus several smaller, independently-verified findings surfaced while tracing false leads during the same debugging session (a :die non-halting cascade, a PowerShell capability preflight gap, a false "another instance running" lock message, a connectivity-prompt CI-safety gap, and two lower-confidence pipreqs/pyproj_deps errorlevel-handling findings, flagged with their actual verification status). - docs/agent-cold-storage.md: two lower-priority, trigger-gated items from the same session (a repair-loop attempt budget cap; broader binary-presence guards). Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_018ZziZDLZLHPqY4N7kCMTBi --- CLAUDE.md | 201 +++++++++++++++++++++++++++++++++++++ README.md | 1 + docs/agent-cold-storage.md | 22 ++++ docs/open-questions.md | 38 +++++++ run_setup.bat | 42 ++++++++ 5 files changed, 304 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 086b2644..20321912 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -947,6 +947,207 @@ but several represent real gaps worth closing before calling the path fully rele `run_setup.bat` next to your scripts, or move your scripts up into this folder" instead of (or alongside) the generic zero-files message. +Items 44-52 below stem from a 2026-08-14 real Windows Sandbox debugging session (two independent +external AI reviews plus direct verification against current source by the acting agent) chasing a +garbled first run (repeated pauses, a PyInstaller build loop with no environment behind it, some +files written and others not). The root cause (Item 44) turned out to be file corruption from the +download method, not the runtime environment; the other items were either incidentally surfaced +while tracing the false leads before that root cause was confirmed, or corroborated an +already-tracked item (Item 40's dead trailing-backslash comparison -- independently re-derived by +the same session, no new information, not re-filed). Each item below was checked against current +source directly, not taken on the reviews' word alone; where a claim could not be confirmed this +way (no live Windows execution available here), that is noted explicitly rather than stated as fact. + +- **Item 44: the Prime-Directive download path serves `run_setup.bat` with broken (LF-only) line + endings, and cmd.exe's goto/call silently misbehaves on the result.** Confirmed directly: a raw + download (GitHub's "Raw" button, or a `raw.githubusercontent.com` link) is 447,375 bytes with + zero CRLF pairs; the same file via a real `git clone` checkout is 452,917 bytes with 5,542 CRLF + pairs -- a delta of exactly one byte per line, matching `.gitattributes`'s own model exactly: + `* text=auto eol=lf` normalizes the STORED blob to LF regardless of the `*.bat text eol=crlf` + override, and that override only affects checkout-time conversion, never the blob GitHub serves + raw. Every natural "just get me the file" path a beginner would take (the Raw button, a + raw.githubusercontent.com link found via search) hands them a corrupted copy; only `git clone` + (or any path that performs a real checkout) gets it right today. + + **Consequence, confirmed against the real symptom triad from the sandbox session**: cmd.exe + resolves `goto`/`call` by relocating to a byte offset associated with the target label; an + LF-only copy of a ~4700-line file with 129 labels drifts that offset by one byte per line + crossed, compounding with each jump. This produces exactly what was observed: a `goto`/`call` + landing on the wrong spot ("the system cannot find the batch label specified" for a label that + genuinely exists), a `%HP_PY%`-dependent line executing before `HP_PY` was ever assigned because + an earlier gating block was skipped by the drift (`'""' is not recognized as an internal or + external command`), and a run that partially completes -- early, purely-sequential code runs + fine, everything past the first mis-resolved jump does not. No CI lane can catch this: every CI + checkout goes through `actions/checkout`, which always applies `.gitattributes`'s `eol=crlf` + conversion, so the broken artifact only ever exists in what a real user downloads, never in what + CI tests. + + **Mitigated this session, not fully fixed**: `run_setup.bat` now self-checks its own line endings + as literally the first thing it does (before any other `goto`/`call` in the file, so the check + itself stays reliable even on a corrupted copy -- see the new block right after `setlocal` at the + top of the file), and fails fast with a clear, actionable message instead of a silent, partial, + undiagnosable run. This does not fix the distribution channel itself -- a user can still land on + a raw link and get the broken file; the check only turns that into a loud, fixable failure instead + of the multi-hour debugging session that surfaced this item. See `docs/open-questions.md` for the + maintainer decision on whether/how to fix distribution itself (pro/con on the `.gitattributes` + options), and README.md's new TL;DR bullet recommending `git clone` in the meantime. + +- **Item 45: gate the build/warnfix/repair block on `HP_PY` actually existing, so a failed + env-create cannot cascade into a doomed PyInstaller build plus multiple repair-loop attempts with + no interpreter behind any of them.** Deliberately scoped narrow -- this is the small, isolated + first bite; Item 46 below is the larger, NOT-small structural issue this is a partial mitigation + for, and the two should not be conflated into one change. + + **Mechanism**: `:die` returns via `exit /b` rather than halting the process (see + `docs/agent-lessons-learned.md`'s `:die` entry), so a genuine env-create failure can fall through + into `:run_entry_smoke` and attempt a full PyInstaller build, warnfix repair round, DLL-bundle + recovery, and hidden-import recovery (each with their own iteration budgets) against an `HP_PY` + that points at a python.exe that does not exist. Every one of those steps is guaranteed to fail + or no-op uselessly in this state; none of it does the user any good, and each failure inside the + loop is itself a `call :die` site that may pause again. + + **Fix**: a single `if not exist "%HP_PY%" (...)` guard at the top of the build/warnfix/repair + block, skipping straight to whatever the existing no-interpreter failure path already is (or a + new one, if none currently exists cleanly for this exact state) instead of attempting any of it. + Small, isolated, and directly kills the "PyInstaller loops while no env/dep work happened" + symptom without touching `:die`'s own 24+ call sites. + +- **Item 46: `:die`'s `exit /b` lets most of its ~31 call sites continue executing afterward, + producing repeated `pause` prompts and further doomed work instead of a single clear stop. NOT a + small slice -- needs its own careful, dedicated scoping pass before touching it.** Matches this + repo's own already-documented mechanism (`docs/agent-lessons-learned.md`'s `:die` entry: "a + caller with no halt/goto after `call :die` simply continues... `HP_BOOTSTRAP_STATE=error` [was + already fixed at the source so the status file stays honest], but nothing stops execution"). + + **Consequence, confirmed against the real sandbox session**: a chain like + `:conda_create_failed -> call :die (pause #1) -> falls through to :conda_create_done -> HP_PY set + to a python.exe that does not exist -> if not exist (call :die, pause #2) -> falls through -> + more code using the broken HP_PY -> call :die (pause #3) -> ...` produces exactly the "hit pause + several times" symptom reported, each pause looking like a fresh, unrelated failure rather than + one root cause cascading. + + **Candidate fix shapes, not yet chosen between**: (a) a global `HP_FATAL` flag set by `:die`, + checked via `if defined HP_FATAL goto :fatal_exit` after every one of the ~24 continuing call + sites; (b) change what `:die` itself does on exit (e.g. a real process-halting `exit`, not + `exit /b`) for the cases where it is known to be called from the top-level call stack rather than + a nested subroutine -- riskier, since the top-level-vs-nested distinction is not always obvious + from a given call site, and a bare `exit` closes the console window immediately for a + double-click user with no chance to read the message first (see the existing `pause`-before-exit + convention this file already relies on); (c) do nothing beyond Item 45's narrower mitigation for + now, since it already kills the specific worst compounding case (repeated build/repair attempts) + even without touching `:die` itself. Given the number of call sites and `:die`'s central, + load-bearing role throughout the file, treat this as EXTREME CAUTION on the same order as the + DLL-bundling/hidden-import repair loops elsewhere in this backlog -- one incremental slice at a + time, not a single sweeping change across all 31 sites. + +- **Item 47: no PowerShell capability preflight beyond bare presence.** The new line-ending + self-check (Item 44's mitigation) added a `where powershell` presence guard as its own + precondition, but that only proves PowerShell exists on PATH, not that it can actually do the + things this bootstrapper needs -- `:emit_from_base64` (used to write every embedded `~*.py`/ + `~*.ps1` helper to disk) needs `[Convert]::FromBase64String` + `[IO.File]::WriteAllBytes`, and + `~failfast_probe.ps1`/`~exe_smokerun.ps1` need `New-Object System.Diagnostics.ProcessStartInfo` + -- all of which a locked-down corporate image (AppLocker/WDAC/Constrained Language Mode) can + block even with PowerShell itself present and on PATH. This was the leading hypothesis in the + sandbox debugging session before the real root cause (Item 44) was confirmed; ruled out for THAT + specific sandbox (confirmed `FullLanguage`, `EMIT OK`, `PSI OK` via direct probing) but not a + dead concern in general -- a genuinely CLM-restricted machine would still hit this today with no + clear diagnostic, just the same opaque "Could not write ~x" pattern the sandbox session initially + (incorrectly, for that session) suspected. + + **Fix**: run the FromBase64String + WriteAllBytes + `New-Object ProcessStartInfo` triple once, + early (after Item 44's line-ending check, before `:define_helper_payloads`), and fail with a + plain-language message naming Constrained Language Mode specifically if it fails, rather than + letting the failure surface piecemeal as five-plus separate "Could not write ~x" messages later. + +- **Item 48: no writable-CWD preflight; `:merge_git_config` writes `.gitignore`/`.gitattributes` + into the app folder before any guard checks the folder is actually writable.** Small, isolated. + `:merge_git_config` (called at line ~82, before `:acquire_lock`) is the first thing in the file + that writes to the app directory itself, and its own write failures are not checked. Fix: a + cheap `type nul > "~wtest.tmp"` + errorlevel check, with a named message pointing at the folder, + placed before `:merge_git_config`'s own call site (right after Item 44's line-ending check is a + natural spot, since both are "can this even run here at all" preconditions). + +- **Item 49: `:lock_is_stale`'s indeterminate PowerShell result is silently treated as "fresh" + (lock held by a live instance), producing a false "another instance of this setup appears to be + running" message instead of a graceful continue.** CONFIRMED directly against current source + (`run_setup.bat` ~lines 5021-5028). The subroutine's own contract comment is explicit: + `exit/b 0 = stale (caller should evict); exit/b 1 = fresh (still held by a live instance)` -- but + the only branch that explicitly sets `HP_LOCK_STALE_RESULT` to a recognized value is the + `'stale'` case; an empty/unexpected PowerShell result (e.g. a transient PowerShell hiccup, not + necessarily anything wrong with the lock itself) falls through to whatever the default trailing + statement is, which -- per the subroutine's own documented two-value contract and no visible + third branch -- reads as "fresh," sending a real user to the "another instance is running, + delete ~bootstrap.lock" message for a condition that has nothing to do with a concurrent run. + + **Fix**: distinguish "explicitly fresh" from "indeterminate" (anything not exactly `'stale'` or + `'fresh'`), and treat indeterminate the same as the already-graceful "could not acquire lock + after evicting" path a few lines below (`[WARN] ... continuing without it.`) rather than as a + hard block. + +- **Item 50: `:cndf_prompt_loop` (the REQ-013 connectivity-check retry prompt) lacks the CI-safe + auto-decline pattern every sibling consent gate in this file already uses.** CONFIRMED directly + against current source (`run_setup.bat` ~lines 5462-5501). Every other consent gate in this file + follows the documented 3-4 branch template (`docs/agent-lessons-learned.md`'s "CI-safe + interactive gates" entry: echo the prompt unconditionally, then an `HP_TEST_*_ANSWER` override, + then an `HP_CI_LANE`/`NOINPUT`/`HP_NONINTERACTIVE` auto-decline, then a real interactive + `set /p`) -- `:cndf_prompt_loop` is a bare `set /p HP_CONN_CHOICE=...` with no such branch at + all. `set /p` against a genuinely closed/EOF stdin (a fully detached CI job) returns empty and + the existing empty-input handling already defaults to offline gracefully -- but against a real, + open, interactive console with nobody present to answer (an unattended real machine where a + human started the bootstrapper, network drops mid-run, and they are not watching), it blocks + waiting for Y/N indefinitely, unlike every sibling gate. + + **Fix**: add the same `HP_TEST_*_ANSWER` / `HP_CI_LANE`/`NOINPUT`/`HP_NONINTERACTIVE` branches + this file's other consent gates already use, defaulting to whichever of retry/offline is safer + unattended (offline, matching the existing empty-input default two lines below). + +- **Item 51: `HP_PIPREQS_RC`'s errorlevel capture, in the direct (non-staging) pipreqs path, has + an intervening `set` command between the pipreqs invocation and the `%errorlevel%` read -- + PLAUSIBLE, NOT CONFIRMED, needs a live-cmd.exe check before acting.** Current source + (`run_setup.bat` ~lines 1323-1326): + ``` + "%HP_PY%" -m pipreqs.pipreqs ... > "%HP_PIPREQS_DIRECT_LOG%" 2>&1 + :pipreqs_direct_done + set "HP_PIPREQS_LAST_LOG=%HP_PIPREQS_DIRECT_LOG%" + set "HP_PIPREQS_RC=%errorlevel%" + ``` + If a plain successful `set` resets `%errorlevel%` to 0 (contested even in general cmd.exe + folklore, and this repo's own `docs/agent-lessons-learned.md` explicitly warns against trusting + static reasoning about cmd.exe semantics without a live test -- three separate past incidents in + this exact file were each "fixed" wrong before a live-cmd.exe fixture caught the real behavior), + `HP_PIPREQS_RC` would always read "0" regardless of pipreqs's real exit code, silently + misclassifying a genuine pipreqs crash. Notably, the SIBLING staging-path capture 80 lines later + (~line 1402) captures `%errorlevel%` on the very next line with no intervening command -- + suggesting this might already be a known-avoided hazard elsewhere in the same file, making the + direct-path instance look like an inconsistency worth resolving even before the exact mechanism + is confirmed. + + **Fix, low-risk regardless of the exact mechanism**: move the `HP_PIPREQS_RC` capture to + immediately follow the pipreqs invocation (before the `HP_PIPREQS_LAST_LOG` set), matching the + already-used safe pattern at the staging call site. Costs nothing even if the hazard turns out + not to be real. **Verification needed before or alongside the fix**: a small live-Windows-CI + fixture confirming whether `set "VAR=literal"` does or does not reset `%errorlevel%`, following + this repo's own established "trust the live test over reasoning" methodology. + +- **Item 52: `tools/pyproj_deps.py`'s exit code 1 is overloaded between its intentional + "no `[project].dependencies` found" contract and a catch-all for any genuinely unexpected + exception, making a real bug in that script indistinguishable from the normal case.** CONFIRMED + directly against `tools/pyproj_deps.py` source and `run_setup.bat`'s own consumption of it + (~lines 1168-1182). The documented contract (exit 0/1/2 = ok/not-found/malformed-TOML) is + correct and intentional -- `run_setup.bat`'s silent no-op on exit 1 is CORRECT for the + "not-found" case, not a bug (an earlier external review of this same code mischaracterized this + as "swallowing a standard exception," which is not accurate -- exit 1 for "not found" is by + design). The real, narrower gap: `pyproj_deps.py`'s own top-level `except Exception: + sys.exit(1)` catch-all means a genuinely unexpected exception ALSO exits 1, so + `run_setup.bat`'s `if errorlevel 1 ( if errorlevel 2 (...) )` structure -- which only logs a + WARN for errorlevel >= 2 -- silently treats a real crash exactly like the benign "nothing to do + here" case. Low severity (the script is small and stable; this would only bite if a future + Python version or TOML edge case triggers an unhandled exception somewhere not already caught by + the script's own narrower `except` blocks) and low priority given that. Fix, if picked up: + either a distinct exit code for the top-level catch-all (e.g. 3), or an unconditional low-tier + log line (not a WARN) on any errorlevel 1 so the fact is at least visible in `~setup.log` for a + future debugging session, without changing user-facing behavior. + ## Cold Storage (promising ideas, deliberately shelved -- revisit only if a named trigger fires) Moved to `docs/agent-cold-storage.md` (2026-07-31, to reduce this file's per-session context diff --git a/README.md b/README.md index 822ac174..870fecee 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ This repository serves as a proof of concept of this new approach. ## TL;DR (Quickstart) - **Windows 10 (1809+)** or newer. +- **Getting `run_setup.bat`:** Use `git clone https://github.com/mixmansoundude/Python_vs_Windows` (or clone your own fork) and copy the file from there -- this preserves the Windows line endings the script needs. Downloading it via GitHub's "Raw" button or a `raw.githubusercontent.com` link can silently corrupt those line endings instead; if that happens, the script now detects it on startup and tells you how to fix it. - **One Folder per Program:** Create a unique folder for your project (e.g., `universal_paperclip_optimizer` or `solve_world_hunger_v2`). - **Avoid Conflicts:** To ensure environment integrity, do not mix independent programs in the same folder. Each program should have its own dedicated folder and its own copy of `run_setup.bat`. - **First run on Windows:** Windows may show "Windows protected your PC" -- click **More info** -> **Run anyway**. If "Run anyway" is absent: right-click the batch -> **Properties** -> check **Unblock** -> **OK** -> run again. diff --git a/docs/agent-cold-storage.md b/docs/agent-cold-storage.md index 64e6ae40..c3a9dc8d 100644 --- a/docs/agent-cold-storage.md +++ b/docs/agent-cold-storage.md @@ -26,6 +26,28 @@ or more SPECIFIC, checkable triggers -- not "eventually," not "never," but "only they stay visible without implying either imminent work or a closed door, and thaw one only when its own named trigger genuinely fires -- do not speculatively build any of these ahead of that. +- **Total build-attempt budget cap across nested repair loops** (hidden-import recovery x + DLL-bundle recovery x REQ-009 provider-cascade tiers, each with its own iteration cap that + compounds with the others -- see CLAUDE.md's former Active Backlog Item 45/46 discussion for the + no-`HP_PY` case this overlaps with). Raised during a 2026-08-14 real Windows Sandbox debugging + session as a theoretical concern (many PyInstaller invocations possible in the worst case with no + single circuit breaker), not a confirmed problem -- the individual loops are each already capped + and this repo's own history with them (CLAUDE.md's DLL-bundling/hidden-import interconnect + entries) shows real, hard-won caution about touching them without strong justification. **Trigger + to thaw**: a real CI run or user report showing the repair-loop product actually causing a + meaningfully long hang or wasted build time, not just the theoretical worst case being + mathematically possible. + +- **Binary presence guards beyond PowerShell** (curl, findstr, robocopy, fc, reg, timeout, ping, + more, sort, expand, tar). Only `choice.exe` and, as of the 2026-08-14 line-ending self-check + (CLAUDE.md's former Active Backlog Item 44), `powershell` are checked for presence before use; + every other external binary this bootstrapper depends on fails as an opaque errorlevel if absent. + Not pursued now: all of these are core Windows components present by default on any Windows 10+ + install this bootstrapper already targets, so the realistic trigger rate is low relative to the + PowerShell case (which has a real, known restriction mechanism -- Constrained Language Mode -- + that these do not). **Trigger to thaw**: a real user report of a missing-binary failure on one of + these specific tools, not a speculative preemptive sweep. + - **PYSPEC-aware venv-vs-embed decision function.** `:try_venv_fallback` currently uses whatever ambient Python is on the machine unconditionally, with no check of whether it actually satisfies `PYSPEC` (the same value `~detect_python.py` already computes for uv/conda/embed). diff --git a/docs/open-questions.md b/docs/open-questions.md index 9f613a6b..64af1abf 100644 --- a/docs/open-questions.md +++ b/docs/open-questions.md @@ -45,3 +45,41 @@ no repair ever attempted. Real, already-emitted log lines back all three states: **Decision needed**: implement this new caveat wording (and if so, in this loop or backlogged), or leave the caveat panel purely generic indefinitely and close this question as "declined"? + +--- + +## 2. Should the distribution channel itself be fixed so a raw download of `run_setup.bat` gets correct (CRLF) line endings, and if so, how? + +**Status: OPEN, raised 2026-08-14.** Background and full mechanism in CLAUDE.md's former Active +Backlog Item 44 (now closed/mitigated -- see `docs/agent-closed-backlog.md` once archived): a raw +download (GitHub's "Raw" button, or a `raw.githubusercontent.com` link) serves `run_setup.bat` +with Unix (LF-only) line endings instead of the Windows (CRLF) endings a real `git clone` checkout +produces, because `.gitattributes`'s `* text=auto eol=lf` normalizes the STORED blob to LF and the +`*.bat text eol=crlf` override only affects checkout-time conversion, never what GitHub serves raw. +cmd.exe's goto/call label-seeking silently misbehaves on the LF-only copy, producing a confusing, +partial, undiagnosable run. A same-session mitigation now makes `run_setup.bat` self-detect this +and fail with a clear message (see the top of the file) -- but the distribution channel itself is +unchanged: a user can still land on a raw link and get the broken file, they just now get told +clearly instead of being left confused. + +**The maintainer explicitly wants**: diffs to stay clean (no line-ending noise from +cross-platform edits), and ideally for a raw download to just work. These two preferences are in +tension, since git's own line-ending normalization is exactly what both protects diffs AND causes +the raw-download breakage -- see the option table below. + +| Option | Raw download works? | Diff cleanliness | Effort | Notes | +|---|---|---|---|---| +| **A. Status quo** -- keep `.gitattributes` exactly as-is (`text=auto eol=lf` + `*.bat`/`*.ps1` `eol=crlf`) | No (confirmed broken) | Best -- this is exactly what `text=auto` exists to guarantee | None | The self-check (Item 44's mitigation) turns the failure loud and actionable instead of silent, but does not prevent it. `git clone` already works correctly today with zero further changes. | +| **B. Make `.bat`/`.ps1` files `-text`/`binary`** (disable git's line-ending conversion for them entirely) | Yes, IF the stored blob is CRLF at the time each commit is made | Worse, and inconsistently so -- diffs are computed against raw bytes, so any commit made from an LF working copy (e.g. an editing tool on Linux/Mac that doesn't explicitly preserve CRLF) re-introduces a whole-file line-ending diff, and risks silently mixing CRLF/LF within one file across edits | Low to set up, but shifts ongoing burden onto every future commit | This is the option that most directly reopens the "noisy diffs" problem the maintainer said they want to avoid -- git's own checkin normalization (which currently fixes this automatically regardless of what edited the file) would be gone. Would need either strict contributor/tooling discipline or a CI check enforcing CRLF on every PR touching these files to be safe, adding friction to what CLAUDE.md's own docs describe as a very frequently edited file. **Not recommended given the stated diff-cleanliness priority.** | +| **C. Publish a GitHub Release with a verified-CRLF asset**, and point users at that instead of the raw blob URL | Yes, and cleanly -- Release assets are not git blobs and are not subject to `.gitattributes` conversion at all | Unaffected -- the repository's own line-ending policy (Option A) stays exactly as-is | Moderate -- needs a CI step (this repo already has the infrastructure for this kind of thing, e.g. the diagnostics-site publish job) that builds/verifies/uploads a CRLF-correct asset on some cadence (every push to main, or every tag) | The only option that achieves "raw download works" without reopening the diff-noise problem. Real but bounded effort; a legitimate future Active Backlog item if this direction is chosen. Self-check (Item 44) stays valuable regardless, as defense-in-depth for anyone who still finds an old raw link. | +| **D. No infrastructure change; just document the correct way to get the file** | No structural fix, but the natural failure mode is closed off by steering users away from it | Unaffected | Essentially free | Already done this session: README's new TL;DR bullet recommends `git clone`. Cheap, available today, but relies on users reading and following it rather than the download "just working" from the most obvious link. | + +**Recommendation (not a decision -- the maintainer's call)**: keep Option A's `.gitattributes` +policy unchanged (protects the diff-cleanliness priority, and is what the existing CLAUDE.md +convention already documents as intentional), do Option D now (already done), and treat Option C +as a real but separate future Active Backlog item if "raw download should just work" is a strong +enough priority to justify the CI effort. Option B is not recommended given the explicit +diff-cleanliness preference. + +**Decision needed**: pursue Option C (and if so, prioritize it as an Active Backlog item), or +accept Option A+D as the steady state and close this question? diff --git a/run_setup.bat b/run_setup.bat index bf04d5d9..ca84fff3 100644 --- a/run_setup.bat +++ b/run_setup.bat @@ -30,6 +30,48 @@ :: ============================================================ @echo off setlocal DisableDelayedExpansion +rem ============================================================ +rem LINE-ENDING SELF-CHECK -- keep this first, before any goto/call in +rem this file. A raw/blob download ^(GitHub's "Raw" button, or a +rem raw.githubusercontent.com link^) serves this file with Unix ^(LF^) +rem line endings instead of the Windows ^(CRLF^) endings a real git +rem checkout produces ^(see .gitattributes^). cmd.exe's goto/call label +rem lookup can silently misbehave on an LF-only copy of a file this +rem size -- wrong-label errors, skipped blocks, corrupted commands -- +rem producing a confusing partial run instead of a clear failure. This +rem check must therefore be self-reliant ^(no goto/call anywhere in +rem this file, since that is exactly what an LF-only copy breaks^) and +rem must run before anything else. +rem ============================================================ +where powershell >nul 2>&1 +if errorlevel 1 ( + echo *** + echo *** [ERROR] PowerShell was not found on this machine. + echo *** This script requires PowerShell to run at all; please + echo *** repair or reinstall it, then run this script again. + echo *** + pause + exit /b 1 +) +powershell -NoProfile -Command "$c=[System.IO.File]::ReadAllText($args[0]);if($c -match [string]@([char]13,[char]10)){exit 0}else{exit 1}" "%~f0" >nul 2>&1 +if errorlevel 1 ( + echo *** + echo *** [ERROR] This copy of run_setup.bat has Unix-style line endings + echo *** ^(LF^) instead of the Windows-style ^(CRLF^) it needs to run, and + echo *** will fail with confusing, partial, hard-to-diagnose errors. + echo *** This usually happens when downloading via the GitHub "Raw" button + echo *** or a raw.githubusercontent.com link, neither of which preserves + echo *** Windows line endings. + echo *** + echo *** Easiest fix: re-download using "git clone", not the Raw button. + echo *** + echo *** Or open this file in an editor that shows line endings + echo *** ^(e.g. Notepad++, VS Code^) and convert it to Windows ^(CRLF^) + echo *** line endings, then save and run this script again. + echo *** + pause + exit /b 1 +) set "DEP_SOURCE=unknown" rem [REQ-026] Argv passthrough escape hatch (docs/plan-cli-interactive-verification.md P1): rem capture trailing arguments (%2-%9) here, before anything else touches %1-%9, and forward From d6c6b1fd179d144be67e5e0876512bd885f4d968 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Aug 2026 02:39:23 +0000 Subject: [PATCH 2/7] Fix line-ending self-check: it was failing on every valid CRLF checkout Two real bugs in the PowerShell one-liner, both confirmed by CI (every lane failed within ~2 minutes) and independently caught by CodeRabbit and Codex review, then reproduced and verified fixed against a real locally-built PowerShell 7 binary (not just reasoned through again): 1. With a string-valued -Command, the trailing "%~f0" argument is NOT bound into $args -- PowerShell parses it as additional command text, not positional data. $args[0] was always empty, so ReadAllText() threw on every single run. Fixed by passing the path through an environment variable (HP_SELF_PATH) instead of a trailing argument. 2. [string]@([char]13,[char]10) does not concatenate the two chars -- PowerShell's default array-to-string conversion joins with $OFS, which defaults to a space, producing "CR SPACE LF" instead of CRLF. So even a correctly-passed path would never have matched. Fixed by using plain single-quoted regex literals ('\r\n', etc.) instead -- simpler than the array-cast approach, and it lets the check reject bare LF and bare CR too, not just confirm at least one CRLF exists. Also addresses two more review findings, both real: - Both pause calls now skip under HP_CI_LANE, matching the pattern :die already uses elsewhere in this file (unconditional pause could hang a non-interactive run whose stdin isn't closed/redirected). - A genuine PowerShell execution failure (as opposed to a real LF-only file) now gets its own distinct message via a third exit code, instead of being misreported as a line-ending problem. Verified via a real PowerShell 7 binary built for this sandbox: the exact command string now in the file correctly exits 0 against this repo's actual CRLF-checked-out run_setup.bat, and exits 1 against a genuine LF-only copy of the same file. Also: two lone apostrophes in new echo text (introduced while writing this) each desynced check_delimiters.py's own quote-tracking the same way the first one did in the previous commit -- fixed the same way. CLAUDE.md Items 49-52 reworded to cite stable labels/subroutine names instead of line numbers, per AGENTS.md's explicit convention (missed in the previous commit), and Item 51's fenced code block now has the blank lines markdownlint expects around it. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_018ZziZDLZLHPqY4N7kCMTBi --- CLAUDE.md | 21 +++++++++++++-------- run_setup.bat | 17 ++++++++++++++--- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 20321912..f0206497 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1070,7 +1070,7 @@ way (no live Windows execution available here), that is noted explicitly rather - **Item 49: `:lock_is_stale`'s indeterminate PowerShell result is silently treated as "fresh" (lock held by a live instance), producing a false "another instance of this setup appears to be running" message instead of a graceful continue.** CONFIRMED directly against current source - (`run_setup.bat` ~lines 5021-5028). The subroutine's own contract comment is explicit: + (`run_setup.bat`'s `:lock_is_stale` subroutine). The subroutine's own contract comment is explicit: `exit/b 0 = stale (caller should evict); exit/b 1 = fresh (still held by a live instance)` -- but the only branch that explicitly sets `HP_LOCK_STALE_RESULT` to a recognized value is the `'stale'` case; an empty/unexpected PowerShell result (e.g. a transient PowerShell hiccup, not @@ -1086,7 +1086,7 @@ way (no live Windows execution available here), that is noted explicitly rather - **Item 50: `:cndf_prompt_loop` (the REQ-013 connectivity-check retry prompt) lacks the CI-safe auto-decline pattern every sibling consent gate in this file already uses.** CONFIRMED directly - against current source (`run_setup.bat` ~lines 5462-5501). Every other consent gate in this file + against current source (`run_setup.bat`'s `:cndf_prompt_loop` label). Every other consent gate in this file follows the documented 3-4 branch template (`docs/agent-lessons-learned.md`'s "CI-safe interactive gates" entry: echo the prompt unconditionally, then an `HP_TEST_*_ANSWER` override, then an `HP_CI_LANE`/`NOINPUT`/`HP_NONINTERACTIVE` auto-decline, then a real interactive @@ -1103,21 +1103,24 @@ way (no live Windows execution available here), that is noted explicitly rather - **Item 51: `HP_PIPREQS_RC`'s errorlevel capture, in the direct (non-staging) pipreqs path, has an intervening `set` command between the pipreqs invocation and the `%errorlevel%` read -- - PLAUSIBLE, NOT CONFIRMED, needs a live-cmd.exe check before acting.** Current source - (`run_setup.bat` ~lines 1323-1326): + PLAUSIBLE, NOT CONFIRMED, needs a live-cmd.exe check before acting.** Current source, right after + the direct pipreqs invocation, at the `:pipreqs_direct_done` label: + ``` "%HP_PY%" -m pipreqs.pipreqs ... > "%HP_PIPREQS_DIRECT_LOG%" 2>&1 :pipreqs_direct_done set "HP_PIPREQS_LAST_LOG=%HP_PIPREQS_DIRECT_LOG%" set "HP_PIPREQS_RC=%errorlevel%" ``` + If a plain successful `set` resets `%errorlevel%` to 0 (contested even in general cmd.exe folklore, and this repo's own `docs/agent-lessons-learned.md` explicitly warns against trusting static reasoning about cmd.exe semantics without a live test -- three separate past incidents in this exact file were each "fixed" wrong before a live-cmd.exe fixture caught the real behavior), `HP_PIPREQS_RC` would always read "0" regardless of pipreqs's real exit code, silently - misclassifying a genuine pipreqs crash. Notably, the SIBLING staging-path capture 80 lines later - (~line 1402) captures `%errorlevel%` on the very next line with no intervening command -- + misclassifying a genuine pipreqs crash. Notably, the SIBLING staging-path capture, a little + further down in the same block right after the staging pipreqs invocation, captures + `%errorlevel%` on the very next line with no intervening command -- suggesting this might already be a known-avoided hazard elsewhere in the same file, making the direct-path instance look like an inconsistency worth resolving even before the exact mechanism is confirmed. @@ -1132,8 +1135,10 @@ way (no live Windows execution available here), that is noted explicitly rather - **Item 52: `tools/pyproj_deps.py`'s exit code 1 is overloaded between its intentional "no `[project].dependencies` found" contract and a catch-all for any genuinely unexpected exception, making a real bug in that script indistinguishable from the normal case.** CONFIRMED - directly against `tools/pyproj_deps.py` source and `run_setup.bat`'s own consumption of it - (~lines 1168-1182). The documented contract (exit 0/1/2 = ok/not-found/malformed-TOML) is + directly against `tools/pyproj_deps.py` source and `run_setup.bat`'s own consumption of it, in + the pyproject.toml dependency-extraction block (`if exist "pyproject.toml" (...)`, the block that + calls `:emit_from_base64 "~pyproj_deps.py" HP_PYPROJ_DEPS`). The documented contract (exit + 0/1/2 = ok/not-found/malformed-TOML) is correct and intentional -- `run_setup.bat`'s silent no-op on exit 1 is CORRECT for the "not-found" case, not a bug (an earlier external review of this same code mischaracterized this as "swallowing a standard exception," which is not accurate -- exit 1 for "not found" is by diff --git a/run_setup.bat b/run_setup.bat index ca84fff3..d525e35e 100644 --- a/run_setup.bat +++ b/run_setup.bat @@ -50,10 +50,21 @@ if errorlevel 1 ( echo *** This script requires PowerShell to run at all; please echo *** repair or reinstall it, then run this script again. echo *** - pause + if not defined HP_CI_LANE ( pause ) + exit /b 1 +) +set "HP_SELF_PATH=%~f0" +powershell -NoProfile -Command "try{$c=[System.IO.File]::ReadAllText($env:HP_SELF_PATH);if($c -match '\r\n' -and $c -notmatch '(?nul 2>&1 +if errorlevel 2 ( + echo *** + echo *** [ERROR] PowerShell could not check the line endings of this file. + echo *** This may mean PowerShell is restricted on this machine -- for + echo *** example, by a Constrained Language Mode policy -- please repair + echo *** or unblock PowerShell, then run this script again. + echo *** + if not defined HP_CI_LANE ( pause ) exit /b 1 ) -powershell -NoProfile -Command "$c=[System.IO.File]::ReadAllText($args[0]);if($c -match [string]@([char]13,[char]10)){exit 0}else{exit 1}" "%~f0" >nul 2>&1 if errorlevel 1 ( echo *** echo *** [ERROR] This copy of run_setup.bat has Unix-style line endings @@ -69,7 +80,7 @@ if errorlevel 1 ( echo *** ^(e.g. Notepad++, VS Code^) and convert it to Windows ^(CRLF^) echo *** line endings, then save and run this script again. echo *** - pause + if not defined HP_CI_LANE ( pause ) exit /b 1 ) set "DEP_SOURCE=unknown" From 4dd31351976de84565a0e292166efa7bbd1aa6c5 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Aug 2026 02:44:53 +0000 Subject: [PATCH 3/7] Track the line-ending self-check's missing CI coverage as a backlog item Both external PR reviews (Codex, citing AGENTS.md directly) correctly flagged that the new self-check has no dedicated CI test of its own -- a normal Actions checkout only ever exercises the CRLF happy path, so a future regression in either the LF-only or PowerShell-failure branch could go unnoticed. Real test-authoring scope, deliberately deferred rather than bolted onto the fix that was already in flight; recorded here so it does not get lost in a PR comment thread. Held locally rather than pushed immediately: batch-check.yml's cancel-in-progress concurrency group would cancel the CI run currently verifying the actual fix (d6c6b1f) if pushed now. Will push once that run completes. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_018ZziZDLZLHPqY4N7kCMTBi --- CLAUDE.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index f0206497..13966e6b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -992,6 +992,20 @@ way (no live Windows execution available here), that is noted explicitly rather maintainer decision on whether/how to fix distribution itself (pro/con on the `.gitattributes` options), and README.md's new TL;DR bullet recommending `git clone` in the meantime. + **Known gap, deliberately not closed in the same slice: the new self-check has no CI coverage of + its own, in explicit tension with AGENTS.md's stated rule that every branch added to + `run_setup.bat` must have a CI test.** Flagged by both external reviews on the PR that shipped + this item (Codex, citing AGENTS.md directly) -- a normal Actions checkout only ever exercises the + CRLF happy path (`actions/checkout` always applies `.gitattributes`'s `eol=crlf` conversion, same + reason no CI lane could catch the original bug), so a future quoting/errorlevel regression in + either the LF-only branch or the PowerShell-execution-failure branch (`errorlevel 2`) could + silently break or remove the check with nothing in CI to notice. Needs a dedicated `HP_TEST_*` + hook (matching this repo's established pattern, e.g. `HP_TEST_FORCE_UV_FAIL`) that deterministically + forces each branch -- an LF-only copy of the running script, and a simulated PowerShell-invocation + failure -- plus an NDJSON row asserting the emitted message and exit code for each. Deliberately + deferred rather than built inline: real test-authoring scope (a new selfapps scenario, workflow + wiring, NDJSON registry update), not a small slice on top of the fix that was already in flight. + - **Item 45: gate the build/warnfix/repair block on `HP_PY` actually existing, so a failed env-create cannot cascade into a doomed PyInstaller build plus multiple repair-loop attempts with no interpreter behind any of them.** Deliberately scoped narrow -- this is the small, isolated From ea30978db7671f287ee3addf762638801e92fd8f Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Aug 2026 02:49:24 +0000 Subject: [PATCH 4/7] Enumerate all three preflight branches in the CI-coverage backlog note Codex flagged the same missing-CI-coverage gap a third time, this round against the PowerShell-absent-from-PATH branch specifically (distinct from the PowerShell-present-but-invocation-fails branch already named). All three are the same underlying gap; the backlog note now names all three so a future pass has a complete checklist instead of two out of three. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_018ZziZDLZLHPqY4N7kCMTBi --- CLAUDE.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 13966e6b..ce526d41 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -997,12 +997,13 @@ way (no live Windows execution available here), that is noted explicitly rather `run_setup.bat` must have a CI test.** Flagged by both external reviews on the PR that shipped this item (Codex, citing AGENTS.md directly) -- a normal Actions checkout only ever exercises the CRLF happy path (`actions/checkout` always applies `.gitattributes`'s `eol=crlf` conversion, same - reason no CI lane could catch the original bug), so a future quoting/errorlevel regression in - either the LF-only branch or the PowerShell-execution-failure branch (`errorlevel 2`) could - silently break or remove the check with nothing in CI to notice. Needs a dedicated `HP_TEST_*` - hook (matching this repo's established pattern, e.g. `HP_TEST_FORCE_UV_FAIL`) that deterministically - forces each branch -- an LF-only copy of the running script, and a simulated PowerShell-invocation - failure -- plus an NDJSON row asserting the emitted message and exit code for each. Deliberately + reason no CI lane could catch the original bug), so a future quoting/errorlevel regression in any + of its three branches -- PowerShell absent from PATH entirely, PowerShell present but the check's + own invocation fails (`errorlevel 2`), or a genuine LF-only copy of the file -- could silently + break or remove the check with nothing in CI to notice. Needs a dedicated `HP_TEST_*` hook + (matching this repo's established pattern, e.g. `HP_TEST_FORCE_UV_FAIL`) that deterministically + forces each of the three, plus an NDJSON row asserting the emitted message and exit code for each. + Deliberately deferred rather than built inline: real test-authoring scope (a new selfapps scenario, workflow wiring, NDJSON registry update), not a small slice on top of the fix that was already in flight. From e27d002d7ab9ed35d4580c3d8f0724e69659f652 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Aug 2026 02:54:14 +0000 Subject: [PATCH 5/7] Drop the remaining line-number citation Codex caught (Item 48) Missed this one in the earlier stable-labels pass -- the preflight block this same PR inserted already pushed :merge_git_config's real line number away from the ~82 cited here. Swept the rest of my new content for the same pattern; the four remaining ~line references in this file are pre-existing (Item 35's own text and the CI Overview section), not something this PR touched, so left alone. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_018ZziZDLZLHPqY4N7kCMTBi --- CLAUDE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index ce526d41..f4eadc9f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1076,7 +1076,7 @@ way (no live Windows execution available here), that is noted explicitly rather - **Item 48: no writable-CWD preflight; `:merge_git_config` writes `.gitignore`/`.gitattributes` into the app folder before any guard checks the folder is actually writable.** Small, isolated. - `:merge_git_config` (called at line ~82, before `:acquire_lock`) is the first thing in the file + `:merge_git_config` (called near the top of the file, before `:acquire_lock`) is the first thing in the file that writes to the app directory itself, and its own write failures are not checked. Fix: a cheap `type nul > "~wtest.tmp"` + errorlevel check, with a named message pointing at the folder, placed before `:merge_git_config`'s own call site (right after Item 44's line-ending check is a From 9c32e84e27c9edcfdfb80c7d894dafe9baa28a47 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Aug 2026 03:09:58 +0000 Subject: [PATCH 6/7] Fix batch.bang.scan CI failure: eliminate literal ! from the PowerShell line The corrected check's negative lookbehind/lookahead regex, '(? Claude-Session: https://claude.ai/code/session_018ZziZDLZLHPqY4N7kCMTBi --- run_setup.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_setup.bat b/run_setup.bat index d525e35e..60c3d41a 100644 --- a/run_setup.bat +++ b/run_setup.bat @@ -54,7 +54,7 @@ if errorlevel 1 ( exit /b 1 ) set "HP_SELF_PATH=%~f0" -powershell -NoProfile -Command "try{$c=[System.IO.File]::ReadAllText($env:HP_SELF_PATH);if($c -match '\r\n' -and $c -notmatch '(?nul 2>&1 +powershell -NoProfile -Command "try{$c=[System.IO.File]::ReadAllText($env:HP_SELF_PATH);$crlf=-join @([char]13,[char]10);$lf=[string][char]10;$cr=[string][char]13;$norm=$c.Replace($crlf,'');if($c.Contains($crlf) -and -not $norm.Contains($lf) -and -not $norm.Contains($cr)){exit 0}else{exit 1}}catch{exit 2}" >nul 2>&1 if errorlevel 2 ( echo *** echo *** [ERROR] PowerShell could not check the line endings of this file. From 2c73030c2989c3514f77347e1cf7cc9950fada99 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Aug 2026 09:54:01 +0000 Subject: [PATCH 7/7] Address CodeRabbit's fresh-review findings: exit code, message accuracy, status file Three real, verified findings against the current preflight code, all fixed: 1. The errorlevel-2 (PowerShell-probe-failure) branch returned exit /b 1, identical to both the PowerShell-missing branch and the invalid-line-endings branch -- defeating the whole point of using a distinct internal errorlevel (2) for this case. Now exits /b 2, so a caller checking the process exit code alone (not just the console message) can also distinguish it. 2. The failure message said "Unix-style line endings (LF)" specifically, but the check (correctly, per the earlier batch.bang.scan fix) also rejects bare CR and mixed CRLF/LF -- a copy with one of those would get an inaccurate diagnosis. Reworded to describe the actual, general contract ("every line ending must be CRLF") rather than the single most common cause, while keeping the "usually happens when..." explanation as guidance since raw download really is the realistic trigger. 3. All three preflight failure branches exit before %STATUS_FILE% is ever set (that happens later, after cd /d) and before :write_status is safe to call (it's call-based, and call is exactly what an LF-only copy of this file cannot reliably do -- the whole reason this preflight avoids call/goto in the first place). A stale ~bootstrap.status.json from an earlier successful run in the same folder would therefore survive a failed preflight untouched, silently misreporting state=ok to anything that reads that file (including this repo's own test harnesses) after a run that never got past its own first check. Fixed with a script-rooted HP_PREFLIGHT_STATUS path (via %~dp0, reliable before cd) and a direct JSON write (matching :write_status's own format) in each of the three failing branches, before pause/exit. Verified: check_delimiters.py clean, ASCII sweep clean, a script mirroring batch.bang.scan's own logic clean, and the PowerShell command string (unchanged by this commit) re-confirmed byte-identical to what was already verified against a real PowerShell 7 binary. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_018ZziZDLZLHPqY4N7kCMTBi --- run_setup.bat | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/run_setup.bat b/run_setup.bat index 60c3d41a..b0dce8a9 100644 --- a/run_setup.bat +++ b/run_setup.bat @@ -43,6 +43,14 @@ rem check must therefore be self-reliant ^(no goto/call anywhere in rem this file, since that is exactly what an LF-only copy breaks^) and rem must run before anything else. rem ============================================================ +rem HP_PREFLIGHT_STATUS is script-rooted via %~dp0 (not CWD-relative, and not the +rem later %STATUS_FILE%/:write_status machinery -- neither exists yet at this point +rem in the file, and :write_status itself is call-based, unsafe to invoke before the +rem line-ending check above has passed). Written directly, only on a preflight +rem failure below, so a stale "ok" status from an earlier successful run in this +rem same folder can never be misread as this run's result if this run's copy of +rem the file cannot even get past its own preflight. +set "HP_PREFLIGHT_STATUS=%~dp0~bootstrap.status.json" where powershell >nul 2>&1 if errorlevel 1 ( echo *** @@ -50,6 +58,7 @@ if errorlevel 1 ( echo *** This script requires PowerShell to run at all; please echo *** repair or reinstall it, then run this script again. echo *** + echo {"state":"error","exitCode":1,"pyFiles":0}> "%HP_PREFLIGHT_STATUS%" if not defined HP_CI_LANE ( pause ) exit /b 1 ) @@ -62,14 +71,16 @@ if errorlevel 2 ( echo *** example, by a Constrained Language Mode policy -- please repair echo *** or unblock PowerShell, then run this script again. echo *** + echo {"state":"error","exitCode":2,"pyFiles":0}> "%HP_PREFLIGHT_STATUS%" if not defined HP_CI_LANE ( pause ) - exit /b 1 + exit /b 2 ) if errorlevel 1 ( echo *** - echo *** [ERROR] This copy of run_setup.bat has Unix-style line endings - echo *** ^(LF^) instead of the Windows-style ^(CRLF^) it needs to run, and - echo *** will fail with confusing, partial, hard-to-diagnose errors. + echo *** [ERROR] This copy of run_setup.bat has invalid line endings. + echo *** Every line ending in this file must be Windows-style ^(CRLF^); at + echo *** least one is not, and the file will fail with confusing, partial, + echo *** hard-to-diagnose errors if run as-is. echo *** This usually happens when downloading via the GitHub "Raw" button echo *** or a raw.githubusercontent.com link, neither of which preserves echo *** Windows line endings. @@ -80,6 +91,7 @@ if errorlevel 1 ( echo *** ^(e.g. Notepad++, VS Code^) and convert it to Windows ^(CRLF^) echo *** line endings, then save and run this script again. echo *** + echo {"state":"error","exitCode":1,"pyFiles":0}> "%HP_PREFLIGHT_STATUS%" if not defined HP_CI_LANE ( pause ) exit /b 1 )