Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -838,6 +838,11 @@ jobs:
- name: Verify native Windows test Python discovery
run: node --no-warnings --experimental-strip-types --test tests/control_plane_ts/test_python_runtime.test.ts

- name: Qualify the canary plan's repository path rendering
run: >-
python -m pytest -q
tests/canary/test_repository_root.py::test_plan_renders_repository_paths_in_canonical_posix_form

- name: Run native Windows lifecycle tests
run: >-
python -m pytest -q
Expand Down
2 changes: 1 addition & 1 deletion loopx/canary/planner.py
Original file line number Diff line number Diff line change
Expand Up @@ -1387,7 +1387,7 @@ def _read_catalog(catalog_path: Path | None = None) -> tuple[Path, str]:

def _display_path(path: Path) -> str:
try:
return str(path.relative_to(REPO_ROOT))
return path.relative_to(REPO_ROOT).as_posix()
except ValueError:
return str(path)

Expand Down
4 changes: 2 additions & 2 deletions loopx/canary/premerge.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ def _public_boundary_changed_files_run(
display_paths: list[str] = []
for path in existing_paths:
try:
display_paths.append(str(path.relative_to(repo_root.resolve())))
display_paths.append(path.relative_to(repo_root.resolve()).as_posix())
except ValueError:
display_paths.append(str(path))
display_argv = ["loopx", "check"]
Expand Down Expand Up @@ -345,7 +345,7 @@ def _display_argv(argv: list[str], *, repo_root: Path = REPO_ROOT) -> list[str]:
for index, value in enumerate(displayed[1:], start=1):
path = Path(value)
try:
displayed[index] = str(path.resolve().relative_to(repo_root.resolve()))
displayed[index] = path.resolve().relative_to(repo_root.resolve()).as_posix()
except (OSError, ValueError):
displayed[index] = value
return displayed
Expand Down
4 changes: 2 additions & 2 deletions loopx/canary/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ def normalize_canary_command(command: str, repo_root: Path | None = None) -> dic
"argv": argv,
"display_argv": _display_argv(argv, repo_root=repo_root),
"injected_args": injected_args,
"script": str(script.relative_to(repo_root)),
"script": script.relative_to(repo_root).as_posix(),
}


Expand All @@ -164,7 +164,7 @@ def _display_argv(argv: list[str], repo_root: Path | None = None) -> list[str]:
for index, value in enumerate(displayed[1:], start=1):
path = Path(value)
if path.is_absolute() and _is_relative_to(path.resolve(), repo_root.resolve()):
displayed[index] = str(path.resolve().relative_to(repo_root.resolve()))
displayed[index] = path.resolve().relative_to(repo_root.resolve()).as_posix()
return displayed


Expand Down
52 changes: 52 additions & 0 deletions tests/canary/test_repository_root.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,58 @@ def test_premerge_uses_selected_checkout_for_nested_plans(tmp_path):
assert Path(check["normalized"]["argv"][1]).is_relative_to(repo / "examples")


def _selected_checks(payload: dict) -> list[dict]:
checks = [check for check in payload.get("selected_checks") or [] if isinstance(check, dict)]
for section in ("catalog_run", "risk_profile_run"):
run = payload.get(section)
if isinstance(run, dict):
checks.extend(check for check in run.get("selected_checks") or [] if isinstance(check, dict))
return checks


def _assert_canonical_path(rendered: str) -> None:
"""A repository-relative path keeps the canonical POSIX spelling.

``PurePosixPath`` would leave a host separator untouched, so the stray
backslash is rejected explicitly rather than through a normalization that
already agrees with the value.
"""

assert "\\" not in rendered, rendered
assert rendered == Path(rendered).as_posix(), rendered


def test_plan_renders_repository_paths_in_canonical_posix_form(tmp_path):
"""A plan's repository-relative paths keep the canonical spelling everywhere.

The catalog, the suite checks, the shipped contract smokes and the public
docs all declare ``python3 examples/<...>.py``. Rendering the same path with
the host separator makes one check read differently per host, which fails
those smokes on Windows and records host-dependent premerge evidence.
"""

repo = _checkout(tmp_path / "repo")
suite = build_canary_smoke_suite_run(repo_root=repo)
gate = build_premerge_validation_gate(
repo_root=repo, changed_files=["loopx/canary/runner.py"],
base_ref="HEAD", execute=False,
)
checks = _selected_checks(suite) + _selected_checks(gate)
rendered = []
for check in checks:
normalized = check.get("normalized") or {}
if normalized.get("ok") is not True:
continue
rendered.append(str(normalized.get("script") or ""))
rendered.extend(
str(token) for token in normalized.get("display_argv") or [] if "examples" in str(token)
)
commands = [str(item) for item in gate["validation_summary"]["selected_commands"]]
assert rendered and commands
for item in [*rendered, *commands]:
_assert_canonical_path(item)


@pytest.mark.parametrize("jobs", [1, 2])
def test_target_checkout_write_guard_detects_and_restores(tmp_path, jobs):
repo = _checkout(tmp_path / "repo", mutate=True)
Expand Down
Loading