# Agent Closed Backlog -- Python_vs_Windows

**This file is NOT auto-loaded into every session's context** (unlike
`docs/agent-lessons-learned.md` and `docs/agent-interconnect.md`, which CLAUDE.md's own
`@import` lines pull in automatically; `docs/agent-ndjson.md` was removed from auto-load on
2026-08-30). Read it on demand when CLAUDE.md's own pointer sends you
here, when you need the full resolution history behind a specific PR/item cited by number, or
when investigating something that "feels like it was already done" and you want the receipt.

Moved out of `CLAUDE.md` directly (2026-07-31) specifically to reduce that file's per-session

# --- head (trimmed) ---

context footprint -- CLAUDE.md is loaded in full every session, and this file alone was ~2600 of
CLAUDE.md's ~4200 lines (>60%) despite being pure historical record with no forward-looking
action attached to any entry. Nothing here needs re-reading by default; it exists so a specific
past decision or fix can be looked up when its details actually matter.

**A `docs/demo-bootstrapper-output.md` "Part N, Scenario N" citation below reflects that doc's
structure AT THE TIME the entry was written, not necessarily its current numbering** -- that file
went through a flow-only reorg pass (2026-08-02) that renumbered every Part and Scenario, and will
likely be reorganized again as it grows. Treat a Part/Scenario citation here as "roughly where to
look," not a precise current coordinate; this file is an append-only historical record, so its own
entries are not retroactively renumbered to track that doc's current structure.

**Five sections below.** "Closed Active Backlog Items" holds items that were promoted out of
`CLAUDE.md`'s own "Active Backlog" section once fully resolved (each keeps its original item
number for cross-reference stability -- other docs cite these by number). "Known Findings"
(moved here verbatim from `CLAUDE.md`, 2026-08-09, Active Backlog Item 34 Loop 2) holds
diagnosed-no-action-warranted investigations -- a question was raised, researched, and answered
with "no code change needed" or "considered and rejected, with reasoning" -- distinct from a
Closed Active Backlog item (resolved with a real change) or an open Active Backlog item (still
pending). "Dependency Strategy Rationale" (moved here 2026-08-09, Active Backlog Item 34 Loop 3)
holds the full multi-paragraph justification behind standing CLAUDE.md rules (the pipreqs version
pin, its invocation strategy, the warnfix `SKIP`-set) -- CLAUDE.md keeps only the load-bearing
rule plus a pointer here. "Closed Backlog" is `CLAUDE.md`'s own pre-existing changelog-style
record of completed feature/fix work, moved here verbatim. "Interconnect Narrative Archive"
(added 2026-08-30, the large-scale compaction pass on the three auto-loaded docs) holds the
"how we found this out" bug-hunt histories that were trimmed out of `docs/agent-interconnect.md`
and `docs/agent-lessons-learned.md` when those files were distilled to current-state-only rules --
those two files' own entries point back here by section title when the provenance matters.

---

## Closed Active Backlog Items

### Item 7 (closed 2026-07-27; moved here 2026-07-31)

- **CI job steps in `batch-check.yml` don't use `if: always()`, so one failing self-test step
   silently cascade-skips every subsequent step in the same job -- observed directly, not
   theorized.** While landing item 6's requirement-1 tests (PR #368), a bug in the new
   `selfapps_pyinstaller_fail.ps1` test itself (see Closed Backlog) caused its own step to fail
   in the `real` lane -- and the resulting `ci_test_results-selftest-real-*` artifact showed only
   ~59 rows total, ending abruptly right at the failing step, with ~40+ unrelated, pre-existing
   test rows expected afterward (the `self.stub.*`/`self.warn.*`/`self.guardrail.*`/
   `self.pep723.*` families and more) completely absent -- not failed, just never run. This is a
   pre-existing, repo-wide characteristic of the whole job (none of the ~50+ steps in the `real`/
   `conda-full` lane's step sequence use `if: always()`), not something introduced by that PR;
   it just happened to be the first time a step failure actually surfaced it. **Not fixed now,
   two reasons**: (a) `continue-on-error: true` is the wrong fix -- these are GATING lane steps
   specifically so a real regression blocks merges, and `continue-on-error` would silently defeat
   that; the real fix is `if: always()` on steps after the risk point (or all of them), which is
   a genuine, valuable hardening pass but touches ~50 existing step definitions across a job that
   already works today when nothing fails -- disproportionate to fold into an unrelated feature
   PR. (b) No user report or observed instance of this actually hiding a REAL regression yet
   (this instance was a test-bug false alarm, immediately visible via the job's own failure
   status) -- worth fixing deliberately, in its own reviewed pass, not as a rushed side effect.
   **Second real-world occurrence, 2026-07-21 (CI run 29829724937, uv lane, job 88632292427):**
   `self.optbuild.offer`'s `accept` scenario step (`tests/selfapps_optimized_build.ps1`) failed on
   a test-authoring bug (see the Closed Backlog entry for the AV-Safe Build Path requirement-9
   work), and because it was the first of three sequential `OPTBUILD_SCENARIO` steps in the same
   job with no `if: always()`, the `forcefail` and `decline` steps never ran at all -- same
   mechanism, same job, different test file.

   **Partially closed, 2026-07-22: the non-gating-lane half of this gap is now fixed; the
   gating-lane half remains open exactly as before.** Re-examined the actual workflow YAML rather
   than relying on memory of the original reasoning, and found `continue-on-error` is already set
   at the JOB level for six of eight matrix lanes (`cache`, `justme-test`, `uv`, `contract-uv`,
   `contract-uv-fail`, `uv-dl-fallback`) -- only `real`/`conda-full` are true gating lanes. This
   means the original "continue-on-error would silently defeat gating" reasoning above applies
   ONLY to steps that can run under `real`/`conda-full`; it never applied to steps restricted to
   the six already-non-gating lanes, since those never gated merges to begin with. Surveyed every
   step's `if:` condition and found 33 steps restricted to non-gating lanes only, of which the PEP
   723 write-back / PVW QuickStart / autopep723-discovery steps already carried per-step
   `continue-on-error: true` (established precedent, not invented for this pass) while 9 others
   (uv-contract assertions, JustMe/download-fallback self-tests, the provider-cascade-exec step,
   both Tier A steps, all 4 `self.optbuild.offer` scenarios) were missing it. Added it to those 9
   -- this is a narrower, safe subset of the originally-declined blanket fix: it changes nothing
   about merge gating (never gated anything) and only stops one failing self-test from hiding its
   siblings' results within the same non-gating-lane job run. The `real`/`conda-full` gating-lane
   half of this backlog item is untouched and remains deliberately deferred for the same two
   reasons as before -- do not extrapolate this fix onto the gating lanes without the same kind of
   deliberate, reviewed pass the original reasoning called for.

   **Two more missed instances found and fixed in a follow-up bug-hunt pass, same day.** A
   dedicated CI-YAML bug-hunt agent re-surveyed the file with fresh eyes and found the `cache`-lane
   `Restore Miniconda cache` step (`uses: actions/cache/restore@v5`) and its sibling `Validate
   restored conda binary` step both missing `continue-on-error: true` despite being restricted to
   the (non-gating) `cache` lane -- the same bug class, just missed in the original 9-step sweep.
   This instance is more severe than the previously-fixed ones: `actions/cache/restore@v5` is a
   real network/service call (GitHub's cache backend has known transient failures) sitting near the
   very START of the job, before roughly a dozen unconditional (non-`always()`) steps including the
   bootstrapper run itself -- a single transient cache-restore failure would silently skip the
   entire cache-lane self-test battery, not just a handful of sibling scenario steps. Fixed the
   same way (added `continue-on-error: true` to both steps); `cache` remains non-gating either way.

   **Re-examined 2026-07-25 per an owner request to raise confidence-to-implement wherever
   possible, gating-lane half only.** Found real, previously-uninvestigated information in both
   directions -- net effect: still not implemented, but for a more precise reason than before.
   - **Lower risk than originally feared, confirmed by actually reading the step definitions
     rather than assuming**: `steps.X.outputs` cross-references and `GITHUB_ENV` state-passing
     inside the `selftest` job are minimal and almost entirely early/self-contained (lane-config
     flags set before the main bootstrap step, plus the already-`always()`-guarded `cache`-lane
     corruption chain) -- confirming the established "every selfapps_*.ps1 test creates its own
     `~selftest_X` scratch directory, independent of sibling steps" convention genuinely holds at
     the CI-step level too, not just within a single test file. This means the originally-feared
     failure mode (step N's crash corrupting step N+1's inputs, producing confusing, misattributed
     secondary failures) is less likely than assumed -- most conda-dependent tests already have
     their own defensive "conda not found" handling (see "Test files that assume conda is present"
     above), which would produce an accurate, if repetitive, `pass=false` rather than a nonsensical
     crash.
   - **A genuinely NEW risk surfaced that the original reasoning never considered**: `if:
     always()` does not just control whether a step RUNS, it can change HOW LONG a job takes to
     FAIL. If the root cause is the top-level "Bootstrap environment" step itself (e.g. Miniconda
     failing to install), a blanket `if: always()` would mean dozens of downstream steps EACH
     attempt their own real sub-bootstrap (each potentially retrying its own slow Miniconda/
     conda-forge network operations) before finally completing, rather than failing fast as today.
     This directly compounds with item 1's own concern (below, now under Periodic Maintenance
     Checks) about CI wall-clock growth -- turning a fast single-step failure into a much longer
     one specifically in the failure case, which is exactly when a contributor is waiting on the
     signal most.
   - **Net assessment: medium, not high, confidence** -- the coupling risk is lower than
     originally assumed, but the newly-found duration-inflation risk is real and unquantified
     (verifying it would need deliberately breaking a real CI run and observing actual timing
     across ~40 downstream steps, not something to do casually). Still correctly deferred; the
     two original reasons for deferral (a wide, ~50-step blast radius; no observed instance of
     this class of gap hiding a real regression yet) both still hold, now with a clearer picture
     of what a future dedicated pass would actually need to verify before shipping.

   **Partially closed, 2026-07-26: the dedicated scoping pass this item asked for, owner-
   requested directly ("Item 7 scoping pass").** Catalogued all 123 steps in the `selftest` job
   by exact `if:` condition, not from memory: ~44 (steps ~84-123, plus the two harness-parse
   steps near the top) already carry `always()`/`failure()` -- the summary/verdict/upload/
   diagnostics tail of the job is already fully hardened. The real risk band is steps ~17-83
   (~67 steps, gated on `env.HP_CACHE_CORRUPTED != '1'` or a specific `matrix.mode` match, no
   `always()`), all downstream of the single "Bootstrap environment (run_setup.bat)" step.
   - **Traced every one of those ~67 steps to its underlying script and found exactly 5 that are
     provably zero-risk** -- they never execute `run_setup.bat` as a subprocess at all, so
     `always()` cannot trigger a redundant real bootstrap for them, only surface a result that
     was previously silently skipped: `selfapps_size.ps1` (a static byte-size tripwire, REQ-017),
     `selfapps_parse_warn_table.ps1` (decodes the embedded `HP_PARSE_WARN` base64 payload as
     static text, never runs the bootstrapper), and the three pure `python -m unittest`/`pytest`
     steps against this repo's own `tests/test_*.py` suite (`test_parse_warn.py`,
     `test_heuristics.py`, the cross-platform pytest step). **Shipped in this pass**: all 5
     converted to `if: always() && <existing lane condition>`, each with an inline comment
     recording why it's safe (verified via `yamllint`/`actionlint`, both clean). Real, if modest,
     immediate hardening at zero duration risk -- a bug in `tools/parse_warn.py` or
     `tools/prep_requirements.py` that coincides with an unrelated bootstrap failure is no longer
     silently invisible.
   - **The remaining ~62 steps all genuinely spin up their own scratch-dir `run_setup.bat`
     invocation** (confirmed via `grep` for each script's actual execution pattern, e.g.
     `cmd /c .\run_setup.bat`, not just a textual mention) -- this is the real, still-open blast
     radius the original backlog text worried about, essentially unchanged in size.
   - **The duration-inflation risk itself is now sharper, not just "lower," thanks to two
     concrete pieces of new evidence.** First, this repo's own already-documented fact (see the
     `:tci_both_failed` Closed Backlog entry) that Miniconda installs to a SHARED, machine-wide
     path (`%PUBLIC%\Documents\Miniconda3`), not a per-test-directory one -- meaning once the main
     bootstrap step gets far enough to install Miniconda successfully, every downstream
     conda-dependent selfapps step finds `conda.bat` already present and skips its own install
     block entirely, at zero extra cost. Second, this session's own owner-authorized CI
     fault-injection experiment (three throwaway-branch probes, see chat history) produced live
     confirmation of BOTH ends of this: a probe that broke `run_setup.bat` before Miniconda is
     ever touched (an unbalanced paren) failed in under a second via cmd.exe's own native parser,
     and a probe that broke it well after Miniconda/conda would already be installed (a corrupted
     PyInstaller flag) also failed fast and deterministically, not via a slow network timeout.
     **Net conclusion: the duration-inflation risk is not "any bootstrap failure," it is
     specifically scoped to the one case where Miniconda itself fails to install** (a real,
     if narrower, failure mode this repo has hit before -- see the REQ-013 connectivity-check
     retry-hardening entry's `conda.anaconda.org` 403 example) -- only THAT case would make every
     downstream conda-dependent step redundantly retry a slow install in lockstep.
   **Closed same day, follow-up slice: the remaining ~36 steps converted too, owner-directed
   ("if confidence is high then proceed to next slice and drive to completion").** Built the
   shared pre-check the paragraph above proposed -- a new `Check Miniconda availability` step
   (`id: conda_avail`, a dual-path `Test-Path` fallback against the shared
   `%PUBLIC%\Documents\Miniconda3\condabin\conda.bat` and its `Scripts\conda.bat` fallback --
   matching `run_setup.bat`'s own `:select_conda_bat` (`CONDA_MAIN`/`CONDA_ALT`) and the existing
   "Validate restored conda binary" step's dual-path check, caught by CodeRabbit on PR #390's
   first real-CI pass -- zero execution/network cost either way) placed right after the main
   bootstrap step -- then converted 36 of the remaining candidates, each gated
   through it with a LANE-AWARE condition, not a uniform one. **Shown here in their final,
   shipped form (`!cancelled()`, not the `always()` these were first written with -- see the
   "Two CodeRabbit findings" paragraph below for why the whole job was converted from one to the
   other in the same pass; this section is kept in sync with the actual condition strings rather
   than describing an intermediate state)**:
   - Steps restricted to `real || conda-full` (22 steps: warnfix family, hidden-import family,
     the PyInstaller-failure family, EXE-smokerun xfails, etc.): `!cancelled() && (matrix.mode ==
     'real' || (matrix.mode == 'conda-full' && steps.conda_avail.outputs.available == 'true'))`.
     The `real` lane is uv-first (see REQ-009 provider order), so it needs no conda gate at all --
     its own redundant-retry worst case is bounded by uv's existing `--retry`/`--max-time` budget
     (seconds to low minutes), not conda's. Only the `conda-full` half of the condition needs the
     pre-check, since that lane unconditionally forces conda for the whole job.
   - Steps restricted to `conda-full` only (5 steps: skiphooks, entry picker, cascade-timed,
     pandas/openpyxl, pip gap-fill): `!cancelled() && matrix.mode == 'conda-full' &&
     steps.conda_avail.outputs.available == 'true'`.
   - Steps that run on every non-corrupted lane (9 steps: empty-repo, single-entry, entry
     selection, isolation, env-name, real-env-smoke, reqspec, UX hardening, system-Python
     consent): `!cancelled() && env.HP_CACHE_CORRUPTED != '1' && (matrix.mode != 'conda-full' ||
     steps.conda_avail.outputs.available == 'true')` -- only conda-full among the lanes this
     condition reaches needs the extra guard.
   **Deliberately NOT converted, three distinct reasons, not oversight**: (1) the three
   pre-bootstrap/setup steps (`Enable Miniconda probe`, `Force conda-only bootstrap`, `Bootstrap
   environment (run_setup.bat)` itself) -- these must run in ORDER, before anything else; `always()`
   on the root step itself is a no-op at best; (2) `Run dynamic tests (if present)` and `Run tests
   (map empty repo to success)` -- confirmed via direct reading that `tests/harness.ps1` `throw`s
   on a missing `~bootstrap.status.json` (lines ~67/71), so `always()`-ing these without ALSO
   hardening that guard risks trading a clean skip for a confusing uncaught-exception failure;
   correctly left for its own small follow-up rather than risking it in this already-large diff;
   (3) `Validate Miniconda before cache save`/`Save Miniconda cache` -- restricted to the `cache`
   lane, which is non-gating (job-level `continue-on-error`), so out of this item's scope entirely.
   Verified via `yamllint`/`actionlint` (clean) and a scripted diff-scope check (exactly 36 `if:`
   lines changed, matching the 36 target steps, no stray edits) before committing -- the same
   "verify the diff touches exactly what's intended" discipline this file's own
   `docs/agent-lessons-learned.md` documents for `run_setup.bat` edits, applied here to YAML.
   Real-CI confirmation (does the `conda_avail` output actually read as `'true'`/`'false'`
   correctly, does a genuine conda-full run behave identically to before when conda IS available)
   is the one thing that can't be verified locally -- watch the next `conda-full` run closely.

   **Two CodeRabbit findings on PR #390's first real-CI pass, both verified independently before
   acting, not taken on faith.** (1) **Real bug, fixed**: the new `conda_avail` check only tested
   `condabin\conda.bat`, but `run_setup.bat`'s own `:select_conda_bat` (`CONDA_MAIN`/`CONDA_ALT`)
   and the pre-existing "Validate restored conda binary" step both already treat
   `Scripts\conda.bat` as an equally valid fallback -- confirmed by reading `:select_conda_bat`
   directly (lines ~1934-1938) before fixing; an install that only landed via the fallback path
   would have made `conda-full`'s own bootstrap succeed while `conda_avail` wrongly reported
   unavailable, silently skipping every newly-gated conda-full self-test. (2) **Real, separately-
   scoped finding, extended and shipped in the same pass**: `always()` keeps a step running even
   after the WORKFLOW ITSELF is cancelled (e.g. a newer push superseding an in-flight run via this
   file's own `cancel-in-progress: true` concurrency group) -- `!cancelled()` is GitHub's own
   documented idiom for "run regardless of prior step outcome, but still respect cancellation."
   This is a real, well-established GitHub Actions semantic distinction, not specific to CI-time
   cost (which this repo's owner has separately said isn't a constraint) -- it's about not letting
   an already-abandoned run's steps keep grinding for no reason. CodeRabbit's own finding was
   scoped to 3 example steps with a note to "apply to the other long-running self-tests in this
   job"; verified this cleanly generalizes to EVERY `always()` in the `selftest` job (84 total,
   none of which hold or release any cross-run resource the way `run_setup.bat`'s own `:acquire_
   lock`/`:release_lock` does -- these are all self-contained diagnostic/self-test steps) and
   applied it uniformly via a scripted replace, not just the 3 cited examples. **A second, real
   bug surfaced during this exact fix, caught by `actionlint` before it shipped**: a bare `if:
   always()` (no `${{ }}` wrapper) is safe YAML since it starts with a letter, but a bare `if:
   !cancelled()` is NOT -- a leading `!` is a YAML tag indicator, so unquoted `!cancelled()`
   fails to parse. 17 of the 84 replacements were bare and needed wrapping in `${{ !cancelled()
   }}`; the other 67 were already inside a `${{ ... }}` compound expression and needed no extra
   wrapping. Verified via `yamllint`/`actionlint` (clean) and an exact before/after occurrence
   count (84 `always()` removed, 84 `!cancelled()` added, 0 stray edits) before committing.
   Deliberately scoped to the `selftest` job only, matching the review's own scope -- the other
   4 jobs in this file (`selftest-gate`, `ndjson-registry-check`, `model-quick-fix`,
   `publish_diag`) have 25 more `always()` occurrences between them, not touched in this pass.
   The suggested dedicated regression test for `conda_avail`'s own dual-path logic (a third
   CodeRabbit comment) was NOT implemented -- disproportionate scope for a 4-line inline check
   with an already-untested precedent in the same file (the "Validate restored conda binary"
   step's own identical dual-path pattern has no dedicated test either).

   **STATUS: gating-lane half of this item is now fully closed, including the one residual noted
   above.** 41 of the ~67 candidate steps were converted in the pass documented above (5 zero-risk
   + 36 lane-aware); the remaining 2 (`Run dynamic tests (if present)`, `Run tests (map empty repo
   to success)`) are now also `!cancelled()`-gated, closing the loop. **Correction to the original
   residual note**: it named `tests/harness.ps1`'s own two `throw` sites (missing
   `.ci_bootstrap_marker` / `~bootstrap.status.json`) as the blocker -- re-reading the actual code
   before fixing it found this was imprecise. Those two `harness.ps1` throws are reached only via
   `Run tests (map empty repo to success)`'s `cmd /c run_tests.bat` call, a genuine subprocess
   boundary: `run_tests.bat` invokes `powershell -File tests\harness.ps1` as its own process, so an
   uncaught `throw` inside it just becomes that process's exit code (captured via `%ERRORLEVEL%`,
   then `$rc`), never an uncaught exception in the CALLING GH Actions step -- that step was already
   safe to `!cancelled()`-gate exactly as-is. The REAL risk was three separate, unrelated `throw`
   statements inline in `Run dynamic tests (if present)`'s own PowerShell block (not in
   `harness.ps1` at all, and not run via a subprocess) -- `~bootstrap.status.json not found`,
   a JSON-parse failure, and `Bootstrap state '{0}' blocks dynamic tests execution`, all reachable
   for the first time once this step could run even after an earlier "Bootstrap environment"
   failure. Fixed by converting those three specifically to `Write-Host "::error::..."` + `exit 1`
   -- same final outcome (the step still fails when the precondition genuinely isn't met), just
   without an uncaught-exception stack trace obscuring the real cause. Two later, unrelated
   `throw`s in the same step (`dynamic_tests.bat`/`.py failed with exit code N`) were deliberately
   left alone -- they only fire once dynamic tests actually ran, meaning the precondition race this
   fix targets never applies to them.

   **CORRECTION, 2026-07-27 (CodeRabbit-flagged on PR #391, verified before acting): the
   `conda_avail` mechanism this item introduced (see the "shared pre-check" paragraph above) was
   itself broken from the day it shipped -- do not treat it as a working part of this item's
   history.** Its premise ("the main bootstrap step installs Miniconda first, so this check only
   ever needs to catch a genuine install failure") is false for this repo's own CI shape: the main
   "Bootstrap environment (run_setup.bat)" step runs against this repo's own empty root (the
   `no_python_files` graceful-exit path), never touches conda, and every downstream selfapps step
   capable of performing the FIRST real install was ALSO gated behind this same check -- a
   circular self-skip that silently disabled ~27 `real/conda-full`-only self-tests on every run
   since this item closed, invisible because a skipped step doesn't fail the job. See Active
   Backlog item 15 for the full diagnosis and fix (PR #391): all 36 `conda_avail`-dependent `if:`
   clauses were reverted to their pre-item-7 unconditional form, keeping only the `!cancelled()`
   half of this item's own work (which IS correct and unaffected by this correction). The
   `conda_avail` step itself remains in the file, unused, pending the re-wiring item 15 defers.

*(Item 5 from the pre-existing "cosmetic log noise/path doubling" debrief note was checked
briefly per standing instruction not to over-invest: no `--distpath`/`--workpath` override or
other structural path-doubling exists in the PyInstaller build invocation. Most likely source is

# --- trimmed ---

- **Python version detection Tier 3 write-back**: Removed `python<3.13` hard-coded cap so
  conda picks the latest available Python (no-hard-coded fallback per REQ-004). After env
  creation, bootstrapper writes runtime.txt in `python-X.Y.Z` format and logs
  `[INFO] runtime.txt written: python-X.Y.Z`. Write-back guarded by `HP_RUNTIME_TXT_PREEXIST`
  so Tier 1 files (pre-existing runtime.txt) are never overwritten. Silent WARN on write
  failure (read-only filesystem). CLOSED by this PR.
- **REQ-004 uv Python version forwarding (Tiers 1-2)**: When PYSPEC is set from runtime.txt
  (Tier 1) or pyproject.toml (Tier 2), the detected Python version is now forwarded to
  `uv venv` via `--python X.Y`. PYSPEC is parsed by inline PowerShell regex to extract the
  lower-bound version from all forms (python=X.Y, python==X.Y, python>=X.Y, python>X.Y).
  Log line: `[INFO] uv: creating venv at .uv_env with Python X.Y`. Covered by new NDJSON
  row `self.contract.uv.pyver` (contract-uv lane). CLOSED by this PR.
- **Edit Detection Sprint (Loops 1-3)**: the earliest fast-path work in this repo's history,
  predating most of the conventions documented above. Loop 1 (PyInstaller build artifact
  cleanup): after a successful build, deletes `build\%ENVNAME%\` and `%ENVNAME%.spec` unless
  a spec file pre-existed (`HP_SPEC_PREEXIST`), logging `[INFO] PyInstaller build artifacts
  cleaned up.` Loop 2 (`HP_DEP_CHECK`/`~dep_check.py`) and Loop 3 (`HP_ENV_STATE`/
  `~env_state.py`) are the dep-check skip and env-state fast paths already summarized under
  "run_setup.bat Rules" above; their runtime-artifact schedule (`~bootstrap.status.json`,
  `~setup.log`, `~environment.lock.txt`, `~env.state.json`) and the `~env.state.json` schema
  both live in AGENTS.md's "Runtime artifact paths" section -- not duplicated here. All three
  loops are complete and live in `run_setup.bat`. CLOSED (this entry condensed from a
  standalone top-level section during a 2026-07 documentation thinning pass).

---

## Interconnect Narrative Archive

Added 2026-08-30 during the large-scale compaction of the three auto-loaded docs plus CLAUDE.md's
own Active Backlog (`docs/agent-interconnect.md`, `docs/agent-lessons-learned.md`,
`docs/agent-ndjson.md`, and CLAUDE.md's own Active Backlog section).
Each subsection below preserves the "how we found this out" detail -- which review round caught it,
which fix attempt was wrong first, the confirming CI run -- that was trimmed from
`docs/agent-interconnect.md`'s current-state entries of the same name. Read on demand only when the
provenance behind a rule actually matters (debugging a regression in the same area, or deciding
whether a "fixed" claim is trustworthy) -- the rule itself lives in the auto-loaded file.

### `:define_helper_payloads` call-order bug (Item 60, PR #455)

Found via a real Windows CI failure that local `pwsh` testing could never have caught, since the
bug depends purely on `call`-statement line order in `run_setup.bat`'s main flow, not on any
runtime state a sandbox could vary. `:merge_git_config` (REQ-015) is called earlier than
`:define_helper_payloads`, and had always been harmless -- until Item 60 added the first payload
use (`HP_MIGRATE_GITATTRIBUTES`) inside it. Reproduced 100% deterministically on every single
bootstrap run in the `real` lane across every scenario. Fixed by moving `call
:define_helper_payloads` to run immediately before `call :merge_git_config`.

### AV-Safe Build Path Tier A + hidden-import interplay (2026-07-21, same day Tier A shipped)

Two separate, mirror-image gaps found the same day: (1) `:hidden_import_recover` had no
`HP_NUITKA_FALLBACK_USED` check before its first real work, so it would unconditionally rebuild via
PyInstaller and silently discard a Nuitka-built EXE -- fixed with an early-skip guard right after
the existing `if not exist "dist\%ENVNAME%.exe"` check, regression-tested by
`tests/selfapps_nuitka_tiera_hidden_skip.ps1` (a fabricated `ModuleNotFoundError: No module named
'nuitka'`, since `nuitka` is guaranteed installed in Tier A's own build interpreter and would
otherwise look like a genuinely fixable target). (2) The warnfix-triggered rebuild (a SECOND
PyInstaller rebuild call site) had NO failure handling at all -- no `if errorlevel 1` check, the
next line unconditionally logged "rebuild complete" regardless of outcome, and nothing re-checked
the EXE existed. Fixed with the same nested if/else shape as other repair loops; on success it also
clears `HP_NUITKA_FALLBACK_USED` (a stale flag from a Tier-A-rescued build earlier in the same run
could otherwise survive and wrongly keep `:hidden_import_recover` skipping repair on an EXE that is
now genuinely PyInstaller-built again). No dedicated CI test for (2) -- a review-pass correctness
fix reusing an already-tested failure-handling shape.

`--collect-submodules` pairing (Item 28, closed 2026-08-08, confirmed via real CI the same day):
found via a real pygrib 2.1.8 failure where `--hidden-import=packaging` alone left
`packaging.version` (a real submodule, never referenced by `packaging/__init__.py` itself) still
missing. New `HP_PYI_HID_COLLECT` accumulator mirrors `HP_PYI_HIDDEN_IMPORTS`'s shape.

### Conda native-DLL bundling repair loop (CLAUDE.md Item 24 and successors -- the longest bug chain in this repo)

PR #414 shipped `:dll_bundle_recover` with the DLL-bundling repair loop for native (conda)
dependencies like `pygrib`'s `eccodes.dll`. CodeRabbit's own review round on that same PR found
FOUR real bugs before it ever reached real CI:
1. **Detection itself was gated on `HP_ENV_MODE=conda`** -- a non-conda provider or Nuitka EXE got
   ZERO detection and ZERO log line, not the documented "detected, repair skipped" state. Fixed by
   restructuring so detection (a cheap `--detect`-mode call, log-parsing only) runs first and
   unconditionally, with the Nuitka-guard and conda-gate each becoming a "detected but here's why
   we can't act" log branch instead of a silent early exit before detection happened.
2. **Tried-list was inline argv (`HP_DLL_TRIED`), not a file** -- a DLL basename can legally
   contain a space or cmd.exe metacharacter (`&`/`|`/`^`), risking command-line injection when
   expanded unquoted. Fixed with a tilde-prefixed file (`~dll_bundle_tried.txt`), appended via
   `type ... >>` (pure byte-copy, never routed through `%VAR%` expansion).
3. **`main()` stalled on the first untried-but-not-on-disk candidate** instead of continuing to try
   successive candidates found later in the same log. Fixed with a small retry loop in `main()`.
4. **The "bundling complete" log line could fire on a genuine rebuild failure** -- `HP_DLL_ITER`
   increments BEFORE the rebuild attempt, so a naive `>= 1` check at the exit label read true on
   both success AND failure paths. Fixed with an explicit `HP_DLL_FAILED` flag set only on a real
   failure branch (also sets `HP_BOOTSTRAP_STATE=error`, mirroring the warnfix-rebuild precedent).

A FIFTH bug (Item 25, found via a LATER CodeRabbit round on the same PR): `:dll_bundle_loop` found
the next candidate BEFORE checking the 3-iteration cap, silently discarding a real, locatable 4th+
DLL instead of reporting `exhausted`. Fixed with `HP_DLL_EXHAUSTED`, set at the cap-check branch
and checked before the "repaired" branch at `:dll_bundle_recover_done`. Low real-world trigger rate
(needs 4+ conda-forge packages each separately needing `--add-binary` in one build, never observed
for a real package) -- covered only by `tests/harness.ps1`'s static wiring check, not a live
trigger.

An EIGHTH bug, found via the first real `cache`-lane run of the whole feature (2026-08-07):
`HP_PY_DIR`'s single trailing backslash (from `%~dpI`) corrupted the argv passed to
`~dll_bundle_scan.py` when immediately followed by another quoted argument -- Python's own
Windows argv parser treated the trailing backslash as escaping the closing quote instead of
closing it, silently merging two arguments into garbage. Confirmed directly against the real
`eccodes-2.48.0-h3bec8ca_0` conda-forge package (genuinely ships `Library\bin\eccodes.dll`) and the
real CI run's own `~environment.lock.txt` (confirmed installed) -- yet the loop still reported "could
not locate." Deterministic on every conda-provider run (not flaky), and plausibly the reason
`self.layered_e2e.chain`'s `chainPass`/`mech3Pass` had never been observed passing at all: the
frozen EXE never got past the corrupted-DLL pygrib import to reach colorama's own separate
hidden-import gap. Fixed with `HP_PY_DIR_ARG` (one extra backslash appended before quoting) at this
one call site. Verified via a faithful Python simulation of the documented Windows argv-parsing
algorithm (`tests/test_dll_bundle_scan.py`'s `HpPyDirArgvQuoting` class) rather than a real Windows
subprocess repro, since Linux's `execve` has no equivalent re-tokenizing step to reproduce the
hazard at all. **Confirmed fixed via real CI** (commit `45ec269`, `cache`-lane run `31208498606`):
`eccodes.dll` genuinely located and bundled for the first time.

Item 28's own fix (above) uncovered a DEEPER gap the same investigation session, closed as Item 29
(2026-08-08, confirmed via real CI the same day): a `--collect-submodules=X` hidden-import fix can
surface a BRAND NEW native-DLL warning that never existed in any earlier build (confirmed via a
real `pyproj`/`proj_9.dll` failure -- once `--collect-submodules=pyproj` bundled `pyproj`'s own
`.pyd` files, PyInstaller's build log showed 9 fresh "could not resolve 'proj_9.dll'" warnings).
Fixed by running `:dll_bundle_recover` a SECOND time per fresh build, after
`:hidden_import_recover`'s own loop finishes. Two independent cross-call state-leak bugs were found
and fixed while wiring this up: (1) `:dll_bundle_recover` unconditionally reset `HP_PYI_DLLBIND` at
its own top, which would silently wipe a FIRST call's accumulated `--add-binary` flags before a
genuine second call's rebuild ran; (2) `:hidden_import_recover` had the identical bug for
`HP_PYI_HIDDEN_IMPORTS`/`HP_PYI_HID_COLLECT`, in TWO places (its own entry AND its own exit
trailer). Both fixed by moving the resets out of the subroutines entirely, into
`:run_entry_smoke`'s own once-per-fresh-build-attempt init block. A CodeRabbit review round on PR
#421 found two more refinements needed: `:hidden_import_recover`'s own rebuild wasn't advancing
`HP_LOG_SIZE_BEFORE` (widening the second DLL scan's window further than necessary, though not
provably causing an actual false-positive for any real observed scenario); and a caller-side early
`if "%HP_EXE_EXIT%"=="0" goto :smokerun_ok` (right after the FIRST `:hidden_import_recover` call)
could skip the entire second-pass block outright whenever the first rebuild happened to already
pass its own smoke run -- defeating the whole point of build-time detection for exactly the case
this feature exists to catch. Removed entirely. **Confirmed via real CI** (PR #421 merge commit
`dcfce1d`, `cache`-lane run `31264219121`): the exact designed sequence played out end to end
(numpy/pyproj hidden-imported, `proj_9.dll` located and bundled on the second pass, colorama fixed
on the second hidden-import pass, EXE verified clean) -- `chainPass` read `true` for the first time
across the whole feature's history.

A NINTH sanitization-specific bug (the `%`/`^` display-safety mechanism, `HP_DLL_DETECTED_SAFE`
etc.) went through THREE rounds of "fixed" before actually working, all confirmed only via a live
`cmd.exe`-executed CI fixture (`tests/harness.ps1`'s `batch.dll_bundle.pct_sanitizer`), never by
reasoning alone: attempt 1 (`set "VAR=%VAR:%%=_%"` doubled-percent substitution) silently produced
an empty string instead of sanitized text; attempt 2 (shell out to PowerShell with a
`-replace '%','_'` literal) put an unpaired literal `%` into the same cmd.exe logical line as a
legitimate `%LOG%` reference, and cmd.exe's `%`-pairing scan (left-to-right, whole-line, no
quote-awareness) paired the lone `%` with `%LOG%`'s own opening `%` instead, silently deleting
everything between them (an even total `%` count is NOT proof of safety -- two lone `%`s can pair
with EACH OTHER instead of each correctly pairing with the real reference). Attempt 3 (build the
percent character via `[char]37` inside PowerShell, so no literal `%` ever appears in the
cmd.exe-visible text) worked, but the final, load-bearing fix was to stop generating cmd.exe-unsafe
`-Command` text at all: the sanitizer moved into a real emitted file
(`tools/dll_pct_sanitize.ps1`, `HP_DLL_PCT_SANITIZE`), invoked via `-File` with env-var names and
output paths as plain argv, removing cmd.exe's tokenizer from the equation entirely. See
`docs/agent-lessons-learned.md`'s "`:log` echoes UNQUOTED" entry for the current-state rule this
established (any future PowerShell one-liner needing a literal `%` should default to `-File`
immediately, not re-attempt `-Command` reasoning).

### `:offer_optimized_build` swap-verification bug (PR #370, fixed same day)

First-shipped version checked `if not exist "dist\%ENVNAME%.exe"` after `move /y` to decide whether
the swap succeeded -- but that's the DESTINATION, the already-working original EXE, which exists
BEFORE the move regardless of outcome. A same-volume FILE `move /y` onto an existing destination is
atomic (fully replaces or fully fails, source consumed only on success), so checking the
destination can never detect a failure (e.g. an AV/indexer lock). Fixed by checking whether the
SOURCE (the temp build) is gone instead, routed through the shared `:optbuild_cleanup` label (fixing
a temp-file leak on the failure path too). New test hook `HP_TEST_FORCE_OPTBUILD_SWAP_FAIL` and the
`swapfail` scenario in `tests/selfapps_optimized_build.ps1` prove the fix.

The embed tier's own directory-swap (`:embed_swap_retry`) initially mirrored this exact "check
whether the source is gone" pattern -- caught before shipping that a DIRECTORY `move` onto an
existing destination silently NESTS the source instead of erroring, so neither "check destination"
nor "check source gone" can detect a failed `rd` (an AV/indexer lock leaving the destination not
fully cleared). Fixed by gating `move` itself on `rd` having genuinely cleared the destination
first, making the subsequent move a pure rename (nesting structurally impossible). NOT CI-confirmed
-- `self.embed.fallback.real` never requests a non-default version through this path, so this
remains static reasoning about documented Windows semantics only.

### Embed tier version-swap dead code (found in a later deep-dive, distinct from the above)

The version-check-and-swap sequence was originally wrapped in one parenthesized
`if not errorlevel 1 ( ... )` block, with a `for /f` loop inside setting `HP_EMBED_SWAP_DIR`/`_TAG`/
`_MINOR` and later code in the SAME block reading `%HP_EMBED_SWAP_DIR%` -- CMD's parse-time `%VAR%`
expansion substitutes using the value from BEFORE the block began, so the read was always empty and
the swap body never actually ran. No test caught it (`self.embed.fallback.real` never requests a
non-default version). Fixed via goto-based dispatch instead of the parenthesized block.

### `runtime.txt` write-back poisoning a cascade re-entry (Item 24, found via real CI evidence 2026-08-07)

Confirmed via a real `self.layered_e2e.chain` cache-lane run: uv resolved `python-3.14.7`,
write-back wrote that to `runtime.txt` the moment uv's venv succeeded, then the uv->conda cascade
(pygrib still failing under uv) re-derived `PYSPEC=python=3.14.7` from that freshly-written file
and forwarded it verbatim to `conda create ... python=3.14.7` -- but conda-forge's own `python`
package release cadence is a wholly separate index from CPython's/uv's and did not carry that exact
patch, producing a hard `PackagesNotFoundInChannelsError` and cascading the run all the way through
embed/venv without ever reaching a real conda environment (so Item 24's own DLL-bundling loop was
never exercised at all in that run, regardless of its own correctness). Fixed same day with
`HP_PYSPEC_WRITEBACK`. Refined the SAME day, before real CI ever confirmed the first version, in
response to both a maintainer question (whether a plain SUBSEQUENT run had the same problem -- it
did not, since `tools/detect_python.py`'s `read_runtime_spec()` already truncates any version
string to major.minor before ever returning a constraint, so only the SAME-PROCESS in-memory
`PYSPEC` reused during a cascade re-entry was ever actually exposed) and an independent CodeRabbit
finding (unconditionally dropping to "no constraint" also discards a genuine user-authored
pyproject/PEP 723 range). Added `HP_PYSPEC_ORIGINAL` (a snapshot taken immediately before each
write-back reassignment) so a genuine range still reaches a cascade target's solver. A genuine,
independent, pre-existing bug was found and fixed in the same pass: `%PYSPEC%` was used UNQUOTED on
both `conda create` command lines -- a PEP 440 range containing live `<`/`>` would be parsed as
real redirection operators by cmd.exe, corrupting the invocation (this predates the session
entirely; a plain non-cascade first run with a ranged `requires-python` would already have hit it
against the original, unmodified code). Fixed by quoting both call sites. Base drop-to-unconstrained
behavior confirmed via real CI (PR #421 merge commit `dcfce1d`, `cache`-lane run `31264219121`,
`pinDropped:true`); the `HP_PYSPEC_ORIGINAL` range-preservation half remains unconfirmed by any real
range-constrained cascade run, since that test's own fixture uses a Tier 3/no-constraint
pyproject.toml.

### Cascade signal reliability investigation (no code change resulted)

A maintainer asked how confident the `HP_CASCADE_CANDIDATE` signal actually is, and whether the
cascade consent gate's default (decline) should change as a result -- specifically, whether the
uv->conda hop deserved a different default than later hops, given conda-forge's real advantage for
native-extension packages. The investigation built a full confidence table (Signal A: still
unresolved after repair; Signal B: an install genuinely failed) with informed-estimate odds per
combination, and reasoned through why only the uv->conda hop has a real mechanism-level
justification (later hops -- embed/venv/system -- all resolve via the same plain PyPI pip uv
already tried, so they mainly help only for environment-specific root causes). Despite this
supporting a case for flipping the default specifically for that one hop, the maintainer decided to
keep `:cascade_consent_gate` exactly as shipped -- the "tell the user their odds of a DIFFERENT tier
helping" idea was considered too, and also not implemented. No code changed as a result of this
investigation; it is recorded here (and pointed to from `docs/agent-interconnect.md`) purely so a
future agent does not re-litigate the same question from scratch without knowing it was already
asked and deliberately left as-is.

### Live-tee async output: three superseded implementations (`~failfast_probe.ps1`/`~exe_smokerun.ps1`)

Found via `tests/test_failfast_probe.py`'s `InteractiveRoundTrip` test (a scripted `input()`/
`print()` conversation): 2/5 local runs showed a LATER round's output appearing BEFORE an EARLIER
round's. Root cause: `Register-ObjectEvent` dispatches each event via
`ThreadPool.QueueUserWorkItem` with no ordering guarantee between queued items (confirmed upstream:
PowerShell/PowerShell#11937). Before that, Microsoft's own documented claim that calling the no-arg
`WaitForExit()` after a timed one guarantees async output handling has completed was ALSO
empirically false for `Register-ObjectEvent` -- a direct `pwsh` repro (5/5 deterministic) showed
the final event (a line flushed only at process exit) firing after both `WaitForExit()` calls
returned, silently truncating the buffer. Both were fixed by abandoning `Register-ObjectEvent`
entirely for a single-in-flight-read polling loop (verified 20/20 clean vs. 2/5 before).

A THIRD, later bug (found during a refinement pass, motivated by the owner independently hitting
the same class of bug with progress dots delayed in a frozen EXE): `StreamReader.ReadLineAsync()`
is line-buffered, so Python's `input(prompt)` -- which flushes its prompt WITHOUT a trailing
newline -- produced zero completed reads for 2+ seconds even though the bytes were provably in the
pipe (confirmed via `CanRead`). Neither prior test (both feed the ENTIRE scripted answer sequence
through stdin essentially instantly) could ever have caught this, since both "surfaced instantly"
and "surfaced only once something else flushed a line" produce the same final captured text --
this is a structural blind spot of any timing-insensitive test. Fixed by switching to chunk-based
`ReadAsync(char[], int, int)`. The NEW regression test built to catch this class of bug (driving
stdin live via a test-controlled pipe, asserting readability before writing) itself then hit a
FOURTH bug on its first real Windows CI run: `select.select()` on the pipe object passed every
local Linux run but failed with `OSError: [WinError 10038]` on 4 separate Windows CI lanes (CPython
`select.select()` on Windows only supports socket objects). Fixed with a background
`threading.Thread` doing blocking `os.read()` into a `queue.Queue`. This whole chain -- 4 distinct
real bugs across roughly a week of work on what looks like "just tee stdout to the console" -- is
why `docs/agent-lessons-learned.md`'s current entry states the final rules bluntly rather than
re-deriving them.

### Paren-hazard in nested echo/rem prose: three real shipped regressions before the rule was fully generalized

PR #408 (commit `fd52a3f`): a stray `(` on one `echo` line and its matching `)` on the NEXT `echo`
line, inside an already-open `if (...)` block, silently closed the block early -- cmd.exe counts
`(`/`)` characters in raw text with no concept of "this paren is just prose," and must do so before
deciding whether to execute or skip the block, so corruption happens regardless of the block's own
runtime condition. `check_delimiters.py` did not catch it (a stray same-file paren pair is
individually balanced under a whole-file LIFO scan) -- fixed by teaching the checker to track
"already nested, opened on an echo line, closes on a DIFFERENT line" specifically.

PR #445 (Item 52): the identical hazard in `rem` comment text, not caught because
`check_delimiters.py`'s own `.bat`/`.cmd` handling treated `rem` lines as fully opaque and skipped
them from paren-scanning entirely (unlike the echo-line handling, which DID scan characters). Broke
all 8 CI lanes simultaneously (a comment 3 levels deep inside real `if (...)` blocks). Fixed for
the specific instance by rewording; the general gap in the checker was closed later as CLAUDE.md
Item 61 (`prose_kind` generalized to cover `rem`, plus two more general fixes found only by running
the extended checker against the real file: `^`-escaped brackets treated as literal, and `'`/`"` on
prose lines treated as inert rather than opening a persistent fake string).

The SAME PR #445 commit shipped a SECOND, independent paren bug in the code added alongside the
rem-comment fix: a `(exit 3)` pair that opens AND closes on the SAME line, inside echo text nested
FOUR levels deep. The prevailing assumption at the time (a same-line, self-contained pair is safe,
per the PR #408 precedent and `check_delimiters.py`'s own `line != last.line` exemption) turned out
to be WRONG for a NESTED same-line pair -- confirmed via a downloaded diagnostics artifact showing
the identical corruption signature as PR #408's original incident. This was fixed by rewording, but
the general question ("does a same-line pair nested inside a real block need the same treatment as
a cross-line one?") stayed open until a dedicated live-`cmd.exe` probe (`tools/probe_paren_hazard.ps1`
+ a `workflow_dispatch`-only CI workflow, CLAUDE.md Item 61, dispatched manually by the maintainer
after the acting agent's own GitHub integration hit a `403` trying to call the dispatch API itself)
tested a full fixture matrix (nesting depth 1-4, with/without redirection prefixes) against real
`cmd.exe` -- EVERY same-line fixture corrupted, including the simplest possible case (one level of
nesting, no redirection). This confirmed the final, unconditional rule now stated in
`docs/agent-lessons-learned.md`: nested is unsafe, full stop, regardless of same-line vs.
cross-line or redirection. `check_delimiters.py`'s `line != last.line` exemption was removed
entirely as a result, surfacing 63 further genuine findings in `run_setup.bat` (on top of the 26
the `rem`-generalization pass had already found), all fixed by rewording with no functional change.