Skip to content

fix: use POSIX paths in auto-discovery CLI output for Windows compat#1018

Merged
danielmeppiel merged 1 commit intomainfrom
fix/windows-path-separators-in-auto-discovery
Apr 28, 2026
Merged

fix: use POSIX paths in auto-discovery CLI output for Windows compat#1018
danielmeppiel merged 1 commit intomainfrom
fix/windows-path-separators-in-auto-discovery

Conversation

@sergio-sisternes-epam
Copy link
Copy Markdown
Collaborator

Problem

The CI/CD Pipeline is broken on Windows (run 25036652822). Two unit tests in test_script_formatters.py fail because Path.__str__() uses backslash separators on Windows:

FAILED test_format_auto_discovery_message_rich_fallback
  AssertionError: 'prompts/hello.md' not found in '[i] Auto-discovered: prompts\hello.md (runtime: copilot)'

FAILED test_format_auto_discovery_message_success
  AssertionError: 'scripts/deploy.prompt.md' not found in '[i] Auto-discovered: scripts\deploy.prompt.md (runtime: codex)'

Fix

Use Path.as_posix() instead of str(path) / f"{path}" in three sites within script_formatters.py and one in script_runner.py. This ensures consistent forward-slash output regardless of platform.

Files changed

  • src/apm_cli/output/script_formatters.py -- 3 interpolation sites in format_auto_discovery_message
  • src/apm_cli/core/script_runner.py -- 1 print statement in execute

Validation

6,627 unit tests pass locally (macOS). The fix is a minimal, surgical change with no behavioural impact beyond normalising path display.

On Windows, Path.__str__() produces backslash separators, causing
format_auto_discovery_message and the script_runner print statement
to output paths like 'prompts\hello.md' instead of 'prompts/hello.md'.
This broke two tests in test_script_formatters.py on the Windows CI job.

Use Path.as_posix() in all three f-string interpolation sites in
script_formatters.py and the direct print in script_runner.py to
ensure consistent forward-slash output across all platforms.

Fixes CI/CD Pipeline failure on windows-latest (run 25036652822).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 28, 2026 14:34
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Normalizes auto-discovery path rendering in CLI output to be stable across platforms (notably Windows), addressing unit test failures caused by backslash path separators.

Changes:

  • Switched auto-discovery message formatting to use Path.as_posix() instead of implicit str(Path) in ScriptExecutionFormatter.
  • Switched the early auto-discovery print() in ScriptRunner.run_script() to use as_posix() for consistent forward-slash output.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/apm_cli/output/script_formatters.py Ensures auto-discovery messages render prompt paths with POSIX separators in both rich and fallback branches.
src/apm_cli/core/script_runner.py Ensures the early auto-discovery print uses POSIX separators for Windows-compatible output and tests.

@sergio-sisternes-epam sergio-sisternes-epam added the panel-review Trigger the apm-review-panel gh-aw workflow label Apr 28, 2026
@github-actions
Copy link
Copy Markdown

APM Review Panel Verdict

Disposition: APPROVE (with one minor pre-merge action: CHANGELOG entry)


Per-persona findings

Python Architect:

This is a routine bug-fix PR (4 lines changed, no new abstractions). One class diagram + one execution-flow diagram apply.

1. OO / class diagram

classDiagram
    direction LR
    class ScriptRunner {
        <<IOBoundary>>
        +run_script(script_name, params) bool
        -_discover_prompt_file(name) Path
        -_detect_installed_runtime() str
        -_generate_runtime_command(runtime, path) str
        -_execute_script_command(command, params) bool
    }
    class ScriptFormatter {
        <<Pure>>
        +format_auto_discovery_message(name, prompt_file, runtime) str
        -_styled(text, style) str
        +use_color bool
        +console Console
    }
    class Path {
        <<ValueObject>>
        +as_posix() str
    }
    ScriptRunner ..> Path : uses (discovered_prompt)
    ScriptRunner ..> ScriptFormatter : delegates formatting
    ScriptFormatter ..> Path : formats for display
    class ScriptRunner:::touched
    class ScriptFormatter:::touched
    classDef touched fill:#fff3b0,stroke:#d47600
Loading

2. Execution flow diagram

flowchart TD
    A([apm run SCRIPT_NAME]) --> B[ScriptRunner.run_script]
    B --> C{script in apm.yml?}
    C -- yes --> D[_execute_script_command]
    C -- no --> E["[FS] _discover_prompt_file(name)"]
    E --> F{prompt found?}
    F -- no --> G[auto-install or error]
    F -- yes --> H["print: [i] Auto-discovered: path.as_posix()"]
    H --> I[_detect_installed_runtime]
    I --> J[_generate_runtime_command]
    J --> D
    D --> K([exit])

    L([format_auto_discovery_message]) --> M{use_color and Rich?}
    M -- yes --> N["Rich Text with prompt_file.as_posix()"]
    N --> O{Rich exception?}
    O -- yes --> P["f-string fallback: prompt_file.as_posix()"]
    O -- no --> Q([return formatted string])
    M -- no --> P
    P --> Q
Loading

3. Design patterns

  • Used in this PR: none -- straight-line procedural change, appropriate for scope.
  • Pragmatic suggestion: the raw print() in ScriptRunner.run_script() (line ~77 of script_runner.py) duplicates the responsibility of ScriptFormatter.format_auto_discovery_message(), which was purpose-built for this exact message. Routing through the formatter would unify path-display logic into one testable surface. Low-cost follow-up; not a blocker for this PR.

CLI Logging Expert: The [i] symbol is correct for an informational discovery message. .as_posix() is applied consistently in all three branches of format_auto_discovery_message() (Rich success path, except Exception fallback, else branch) -- the fix is thorough. The raw print() in script_runner.py is a pre-existing anti-pattern (bypasses CommandLogger/ScriptFormatter lifecycle) that predates this PR; out of scope here but worth a follow-up issue.


DevX UX Expert: Windows CLI output showing backslash paths (prompts\hello.md) looks foreign and harms copy-paste ergonomics. Forward slashes match the convention of npm, cargo, pip, and every major CLI tool. No command surface, flags, or help text changed; no cli-commands.md update needed. Pure UX improvement for a significant platform.


Supply Chain Security Expert: path.as_posix() is a string rendering call on an already-resolved Path object. No file I/O, no path traversal, no containment guards needed. No credentials or sensitive values appear in this display path. Zero security surface change.


Auth Expert: Not activated -- the two changed files (script_runner.py, script_formatters.py) handle prompt discovery and output rendering exclusively; no auth, token management, credential resolution, or host classification logic is touched.


OSS Growth Hacker: This fix protects the "first 5 minutes" funnel for Windows users. Garbled backslash paths on a first apm run are a silent conversion killer -- a new Windows user seeing broken output is less likely to continue and less likely to recommend APM. Side-channel to CEO: the CHANGELOG entry at release can be bundled as a "Windows compatibility" beat. The fact that CI caught this regression (run 25036652822) also validates the cross-platform matrix investment -- worth reinforcing in community communications.


CEO arbitration

All five mandatory specialists APPROVE unanimously; there are no disagreements to arbitrate. The fix is a correct, minimal, surgical response to a real Windows CI failure with no behavioral regressions across architecture, output UX, developer ergonomics, or security. The OSS Growth Hacker is right that Windows first-run is a concrete conversion surface worth protecting. One required pre-merge action: add a CHANGELOG entry under [Unreleased] per repo conventions -- every behavior-changing PR needs one. A follow-up issue tracking the print()-in-core inconsistency is recommended but does not block this fix.


Required actions before merge

  1. Add a CHANGELOG entry under ## [Unreleased] in CHANGELOG.md, e.g.:
    ### Fixed
    - Auto-discovery path display now uses forward slashes on Windows (`script_runner.py`, `script_formatters.py`) (#1018)
    

Optional follow-ups

  • Open a follow-up issue to route the raw print() in ScriptRunner.run_script() through ScriptFormatter.format_auto_discovery_message() -- this would consolidate all path-display logic into one testable site and bring script_runner.py in line with the CommandLogger/ScriptFormatter output architecture.
  • Consider adding a Windows-specific test assertion in the existing test_format_auto_discovery_message_* tests that explicitly passes a Windows-style Path (e.g., PureWindowsPath('prompts\\hello.md')) to confirm .as_posix() is invoked, hardening against future regressions on this surface.

Generated by PR Review Panel for issue #1018 · ● 667.8K ·

@danielmeppiel danielmeppiel added this pull request to the merge queue Apr 28, 2026
Merged via the queue into main with commit a26c2e7 Apr 28, 2026
27 checks passed
@danielmeppiel danielmeppiel deleted the fix/windows-path-separators-in-auto-discovery branch April 28, 2026 16:14
danielmeppiel pushed a commit that referenced this pull request Apr 29, 2026
Promotes [Unreleased] -> [0.11.0] - 2026-04-29 and bumps
pyproject.toml + uv.lock to 0.11.0.

Version-bump rationale: 0.11.0 (minor bump) chosen over 0.10.1 because
this release ships one BREAKING removal (`apm marketplace build` -> exits 2,
use `apm pack`) plus several net-new features (Dev Container Feature,
Codex project-scoped MCP, `marketplace:` block in apm.yml, `apm pack`
unification, multi-org `apps[]`). Strict semver in 0.x: minor for
features-with-break, patch only for bugfixes.

Milestone admin (done out-of-band):
- Renamed milestone #8 `0.10.1` -> `0.11.0`
- Created milestone #9 `0.12.0` as next-up bucket
- Moved 43 open items (42 issues + 1 open PR #999) from `0.11.0` -> `0.12.0`
- 6 closed items stay in `0.11.0`

PRs shipping in 0.11.0 (22 commits since v0.10.0):

User-facing features:
- #1042/#722 `apm pack` unifies bundle + marketplace.json
                   (BREAKING: `apm marketplace build` removed)
- #1038       `marketplace:` block in apm.yml + `apm marketplace migrate`
- #803  /#502 Codex project-scoped MCP (`.codex/config.toml`) + user-scope primitives
- #861        Dev Container Feature `ghcr.io/microsoft/apm/apm-cli`
- #982/#984   shared/apm.md `apps:` array for cross-org private packages
- #820        `target:` in apm.yml validates at parse time
- #1032       `apm marketplace add` honors manifest.name (Claude Code parity)
- #1000/#998/#994 unified `--policy` / `--policy-source` accepted forms

User-facing fixes:
- #1015 ADO Entra ID auth + `apm install --update` pre-flight abort
- #1019/#1020 GEMINI.md only created when target requested
- #1008 marketplace producer respects GITHUB_HOST + multi-host URL forms
- #1018 POSIX paths in auto-discovery output (Windows compat)
- #996  drop stray 'specify' from generated file footer

Maintainer tooling:
- #1043 NOTICE.md per CELA template
- #1045/#1044 NOTICE drift gate + license-policy gate in CI
- #1033 shared/apm.md `[a b]` import-input repair (gh-aw#29076 paper-cut)
- #1030 panel workflows skip-don't-fail on unmatched labels; gh-aw v0.71.1
- #1026 shared/apm.md recompiled to apm-action v1.5.0 + bundles-file
- #1022 review-panel: true fan-out + binary verdict + label automation
- #918  complexity audit + benchmarks suite
- #1002 CodeQL clear-text-storage false-positive resolved (token -> placeholder)

Files changed:
- pyproject.toml: 0.10.0 -> 0.11.0
- uv.lock:        regenerated (version field only)
- CHANGELOG.md:   [Unreleased] promoted to [0.11.0] - 2026-04-29

NOTICE drift check passes against the bumped lockfile.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@danielmeppiel danielmeppiel added this to the 0.11.0 milestone Apr 29, 2026
danielmeppiel pushed a commit that referenced this pull request Apr 29, 2026
Promotes [Unreleased] -> [0.11.0] - 2026-04-29 and bumps
pyproject.toml + uv.lock to 0.11.0.

Version-bump rationale: 0.11.0 (minor bump) chosen over 0.10.1 because
this release ships one BREAKING removal (`apm marketplace build` -> exits 2,
use `apm pack`) plus several net-new features (Dev Container Feature,
Codex project-scoped MCP, `marketplace:` block in apm.yml, `apm pack`
unification, multi-org `apps[]`). Strict semver in 0.x: minor for
features-with-break, patch only for bugfixes.

Milestone admin (done out-of-band):
- Renamed milestone #8 `0.10.1` -> `0.11.0`
- Created milestone #9 `0.12.0` as next-up bucket
- Moved 43 open items (42 issues + 1 open PR #999) from `0.11.0` -> `0.12.0`
- 6 closed items stay in `0.11.0`

PRs shipping in 0.11.0 (22 commits since v0.10.0):

User-facing features:
- #1042/#722 `apm pack` unifies bundle + marketplace.json
                   (BREAKING: `apm marketplace build` removed)
- #1038       `marketplace:` block in apm.yml + `apm marketplace migrate`
- #803  /#502 Codex project-scoped MCP (`.codex/config.toml`) + user-scope primitives
- #861        Dev Container Feature `ghcr.io/microsoft/apm/apm-cli`
- #982/#984   shared/apm.md `apps:` array for cross-org private packages
- #820        `target:` in apm.yml validates at parse time
- #1032       `apm marketplace add` honors manifest.name (Claude Code parity)
- #1000/#998/#994 unified `--policy` / `--policy-source` accepted forms

User-facing fixes:
- #1015 ADO Entra ID auth + `apm install --update` pre-flight abort
- #1019/#1020 GEMINI.md only created when target requested
- #1008 marketplace producer respects GITHUB_HOST + multi-host URL forms
- #1018 POSIX paths in auto-discovery output (Windows compat)
- #996  drop stray 'specify' from generated file footer

Maintainer tooling:
- #1043 NOTICE.md per CELA template
- #1045/#1044 NOTICE drift gate + license-policy gate in CI
- #1033 shared/apm.md `[a b]` import-input repair (gh-aw#29076 paper-cut)
- #1030 panel workflows skip-don't-fail on unmatched labels; gh-aw v0.71.1
- #1026 shared/apm.md recompiled to apm-action v1.5.0 + bundles-file
- #1022 review-panel: true fan-out + binary verdict + label automation
- #918  complexity audit + benchmarks suite
- #1002 CodeQL clear-text-storage false-positive resolved (token -> placeholder)

Files changed:
- pyproject.toml: 0.10.0 -> 0.11.0
- uv.lock:        regenerated (version field only)
- CHANGELOG.md:   [Unreleased] promoted to [0.11.0] - 2026-04-29

NOTICE drift check passes against the bumped lockfile.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
danielmeppiel added a commit that referenced this pull request Apr 29, 2026
* chore(release): cut 0.11.0

Promotes [Unreleased] -> [0.11.0] - 2026-04-29 and bumps
pyproject.toml + uv.lock to 0.11.0.

Version-bump rationale: 0.11.0 (minor bump) chosen over 0.10.1 because
this release ships one BREAKING removal (`apm marketplace build` -> exits 2,
use `apm pack`) plus several net-new features (Dev Container Feature,
Codex project-scoped MCP, `marketplace:` block in apm.yml, `apm pack`
unification, multi-org `apps[]`). Strict semver in 0.x: minor for
features-with-break, patch only for bugfixes.

Milestone admin (done out-of-band):
- Renamed milestone #8 `0.10.1` -> `0.11.0`
- Created milestone #9 `0.12.0` as next-up bucket
- Moved 43 open items (42 issues + 1 open PR #999) from `0.11.0` -> `0.12.0`
- 6 closed items stay in `0.11.0`

PRs shipping in 0.11.0 (22 commits since v0.10.0):

User-facing features:
- #1042/#722 `apm pack` unifies bundle + marketplace.json
                   (BREAKING: `apm marketplace build` removed)
- #1038       `marketplace:` block in apm.yml + `apm marketplace migrate`
- #803  /#502 Codex project-scoped MCP (`.codex/config.toml`) + user-scope primitives
- #861        Dev Container Feature `ghcr.io/microsoft/apm/apm-cli`
- #982/#984   shared/apm.md `apps:` array for cross-org private packages
- #820        `target:` in apm.yml validates at parse time
- #1032       `apm marketplace add` honors manifest.name (Claude Code parity)
- #1000/#998/#994 unified `--policy` / `--policy-source` accepted forms

User-facing fixes:
- #1015 ADO Entra ID auth + `apm install --update` pre-flight abort
- #1019/#1020 GEMINI.md only created when target requested
- #1008 marketplace producer respects GITHUB_HOST + multi-host URL forms
- #1018 POSIX paths in auto-discovery output (Windows compat)
- #996  drop stray 'specify' from generated file footer

Maintainer tooling:
- #1043 NOTICE.md per CELA template
- #1045/#1044 NOTICE drift gate + license-policy gate in CI
- #1033 shared/apm.md `[a b]` import-input repair (gh-aw#29076 paper-cut)
- #1030 panel workflows skip-don't-fail on unmatched labels; gh-aw v0.71.1
- #1026 shared/apm.md recompiled to apm-action v1.5.0 + bundles-file
- #1022 review-panel: true fan-out + binary verdict + label automation
- #918  complexity audit + benchmarks suite
- #1002 CodeQL clear-text-storage false-positive resolved (token -> placeholder)

Files changed:
- pyproject.toml: 0.10.0 -> 0.11.0
- uv.lock:        regenerated (version field only)
- CHANGELOG.md:   [Unreleased] promoted to [0.11.0] - 2026-04-29

NOTICE drift check passes against the bumped lockfile.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore(changelog): tighten 0.11.0 entries to lead with user impact

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore(changelog): move Dev Container Feature to Maintainer tooling (not yet published)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore(changelog): de-dupe within 0.11.0 (combine #722 Removed bullets, drop #820 Fixed pointer)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

panel-review Trigger the apm-review-panel gh-aw workflow

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants