Repository navigation
[Bug]: native Windows canary premerge/smoke-suite renders repository-relative script paths with the host separator and fails the gate's own contract smokes #6450
Description
Preflight
-
I searched existing issues and discussions for this behavior.
-
This report is not an unpatched security vulnerability.
-
The report and attachments contain no credentials or private/internal material.
-
Issue origin: observed or reproduced in a real environment
-
LoopX version or commit:
f713e0f07(main), source checkout on native Windows 11 (PowerShell, cp936 console) -
Host or runtime surface: not host-specific (source-checkout tooling; reproduced on native Windows)
-
Area: build, packaging, installer, or CI (canary premerge validation and the public smoke suite)
Impact, First
The canary premerge gate and the public smoke suite render every repository-relative script path with the host path separator, while the catalog, the suite checks, the shipped contract smokes and the docs all declare the canonical python3 examples/<...>.py POSIX spelling.
On native Windows this makes loopx canary premerge — the risk-based validation set AGENTS.md mandates ("Before self-merging non-trivial LoopX changes, run loopx canary premerge --from-git-diff or an equivalent risk-based validation set") — red for every diff, and not because of the diff:
$ loopx canary premerge --changed-file loopx/quota.py --profile canary-runner --no-progress --format json
rc=1 gate: failed risk_profile_run: 3 of 8 selected checks failed
python3 examples/canary/catalog-run-e2e-smoke.py rc=1
python3 examples/canary/premerge-validation-gate-smoke.py rc=1
python3 examples/canary/smoke-suite-runner-smoke.py rc=1
- Who is harmed: every contributor or maintainer who validates a change from a Windows checkout. They cannot obtain a green mandated premerge gate, so they must hand-diff their failure set against a clean baseline to prove their own diff is neutral. That is the same cost issue [Bug]: the pinned top-level module budget is already exceeded on main (150 measured against 147) #5685 records for the module-budget gate ("every PR based on current main inherits that failure ... every contributor must diff failure sets"). Merged PR fix(benchmark): run the EdgeBench report adapter and suite on native Windows #6368 had to record these exact two smokes as
manual | blockedpre-existing host failures in its validation table, and each new Windows-host work item pays it again. - How severe: it is not a display defect. The gate's own contract smokes reject the rendering, so the validation surface a PR is required to record (
selected_commands,normalized.script,normalized.display_argv) is host-dependent: a Windows run and a Linux run of the same diff produce different command strings, and four shipped public smokes fail.docs/development/good-smokes.mdtreats a public smoke as a durable regression contract, not a host-conditional one. - How large: 3 of the 8
canary-runnerrisk-profile checks for any control-plane diff are the gate's own smokes, plussmoke-suite-subdir-discovery-smoke.py; and--suite full-public(release validation) sweeps the same family. The defect is invisible on POSIX hosts, wherestr(relative)andas_posix()are identical, so only the Windows lane can see it — and the Windows lane does not run this family.
Problem
Four shipped canary surfaces render a repository-relative path with str(path.relative_to(repo_root)):
loopx/canary/runner.py— the normalized check'sscriptfield and the_display_argvpath entries;loopx/canary/premerge.py—_display_argvand the public-boundary--scan-pathrendering;loopx/canary/planner.py—_display_path, used for catalogsourceentries.
Every other renderer in the same package already emits the canonical POSIX form: runner._smoke_script_relative, runner._smoke_script_filter_keys, runner._matches_modules and all of loopx/canary/maintainability_ratchet.py use .as_posix(), and loopx/canary/smoke_health.py even compensates for the inconsistent field with an explicit .replace("\\", "/"). The catalog itself declares python3 examples/..., and so does every command in docs/.
So the defect is not that the plan is un-runnable on Windows — argv keeps the absolute path and execution is unaffected. It is that the declared and recorded command identity of a check is host-dependent, which the contract smokes correctly refuse.
Minimal reproduction
On native Windows, from a source checkout at f713e0f07:
python examples/canary/smoke-suite-subdir-discovery-smoke.py
python examples/canary/smoke-suite-runner-smoke.py
python examples/canary/catalog-run-e2e-smoke.py
python examples/canary/premerge-validation-gate-smoke.py
loopx canary premerge --changed-file loopx/quota.py --profile canary-runner --no-progress --format json
Expected behavior
Repository-relative paths in the canary plan and its evidence use the repository's canonical POSIX spelling on every host, exactly as the catalog, the suite checks and the shipped contract smokes declare. All five commands above exit 0, and loopx canary premerge reports the same command identity for a diff whether it was validated on Windows or on Linux.
Actual behavior
All five commands fail on native Windows with the host separator in the plan payload:
$ python examples/canary/smoke-suite-subdir-discovery-smoke.py
AssertionError: {'...': 'selected_checks': [{'...': 'normalized': {'command': 'python3 examples/canary/smoke-suite-subdir-discovery-smoke.py',
'display_argv': ['python3', 'examples\\canary\\smoke-suite-subdir-discovery-smoke.py'],
'script': 'examples\\canary\\smoke-suite-subdir-discovery-smoke.py'}}]}
# assert 'examples\\canary\\smoke-suite-subdir-discovery-smoke.py' == 'examples/canary/smoke-suite-subdir-discovery-smoke.py'
$ python examples/canary/smoke-suite-runner-smoke.py
KeyError: 'examples/canary/canary-promotion-readiness-smoke.py' # by_script is keyed by the host-spelled script field
$ python examples/canary/catalog-run-e2e-smoke.py
AssertionError: {'...': 'normalized': {'command': 'python3 examples/control_plane/promotion-readiness-readmodel-smoke.py',
'display_argv': ['python3', 'examples\\control_plane\\promotion-readiness-readmodel-smoke.py'],
'script': 'examples\\control_plane\\promotion-readiness-readmodel-smoke.py'}}
$ python examples/canary/premerge-validation-gate-smoke.py
AssertionError: 'python3 examples/control_plane/interaction-contract-state-machine-smoke.py' not in
[..., 'python3 examples\\control_plane\\interaction-contract-state-machine-smoke.py', ...]
Note the payload is internally inconsistent: normalized.command keeps the declared POSIX form while normalized.display_argv and normalized.script carry the host separator, so any consumer that joins display_argv or keys on script disagrees with the check's own declared command. tests/test_smoke_suite.py already keys pytest ids on normalized["script"], so the ids differ per host there too.
Sanitized diagnostics
$ python --version
Python 3.12.12
$ loopx canary premerge --changed-file loopx/quota.py --profile canary-runner --no-progress --format json
gate: failed (preview_only); risk_profile_run: selected 8, failures 3
The three failures are examples/canary/catalog-run-e2e-smoke.py, examples/canary/premerge-validation-gate-smoke.py and examples/canary/smoke-suite-runner-smoke.py; each fails identically at the base revision and with no local changes. Other checks that failed on this host during the same run (examples/control_plane/hot-path-interface-budget-smoke.py, examples/control_plane/claude-goal-mode-hook-policy-smoke.py style POSIX-shim cases) are separate, pre-existing host failures and are not part of this report.
Additional context
- Sibling, already-merged work: fix(benchmark): run the EdgeBench report adapter and suite on native Windows #6368 fixed the same class of "native Windows cannot run the documented check" gap for the EdgeBench report adapter, and recorded these two canary smokes in its own PR body as pre-existing host failures.
- The contract this report relies on: the catalog's declared commands,
loopx/canary/runner.py::_smoke_script_check, the four shipping smokes above, and the repo-relative POSIX convention used byloopx/canary/maintainability_ratchet.pyand the CI workflows.
Activity
claims: backfill agent-h5r9 issue loopx-project#6450 PR loopx-project…
fix(canary): render repository-relative plan paths in canonical form