Skip to content

refactor: complexity audit -- eliminate god-method forks, O(n^2) loops, redundant I/O#918

Merged
sergio-sisternes-epam merged 13 commits intomicrosoft:mainfrom
sergio-sisternes-epam:audit/complexity-review-worktree
Apr 28, 2026
Merged

refactor: complexity audit -- eliminate god-method forks, O(n^2) loops, redundant I/O#918
sergio-sisternes-epam merged 13 commits intomicrosoft:mainfrom
sergio-sisternes-epam:audit/complexity-review-worktree

Conversation

@sergio-sisternes-epam
Copy link
Copy Markdown
Collaborator

@sergio-sisternes-epam sergio-sisternes-epam commented Apr 24, 2026

Complexity Audit & Code Quality Improvements

Summary

Comprehensive codebase quality audit covering algorithmic improvements, god function decomposition, exception hygiene, and performance benchmarks.

Commits (10 total)

Commit Description
Round 1 refactoring 6 algorithmic phases: lockfile dedup, console singleton, reverse-dep O(n^2)->O(n), discovery scan, registry cache, NullCommandLogger
Benchmarks (3 iterations) 160 tests: 154 benchmarks + 6 scaling guards covering install, compilation, security, resolver
WI-1: Exception hygiene 7 bare except: replaced with except Exception:, debug logging for silent handlers
WI-4a-d: God function splits reference.py (110->22 stmts), audit.py (290->18+49+67), deps/cli.py (135->59), script_runner.py (124->20)
P1 test gaps (Round 2) 9 tests for ScriptExecutionFormatter + CLAUDE.md injection
WI-2: Downloader decomposition github_downloader.py split into 3 modules: git_remote_ops.py (ref parsing), download_strategies.py (DownloadStrategyManager), slimmed orchestrator (2,669->2,068 lines)
WI-3: Install decomposition install() 555-line god function split into dispatcher + _install_apm_packages() + _handle_mcp_install() + _post_install_summary() with InstallContext dataclass
P1 test gaps (WI-2/WI-3) 17 tests: InstallContext dataclass validation + _resolve_package_references() mutation contract

Findings resolved

ID Severity Finding Resolution
B1 BLOCKING install() 27-param/378-stmt god function WI-3: decomposed into 4 focused helpers + InstallContext dataclass
B2 BLOCKING github_downloader.py 2,669-line god file WI-2: split into 3 modules with backward-compat stubs
B3 BLOCKING Bare except: catches KeyboardInterrupt WI-1: replaced with except Exception:
B4 BLOCKING Silent auth fallback WI-1: added logger.debug()
W2 WARNING reference.py parse chain 110 stmts WI-4a: split into 3 focused parsers
W3 WARNING audit.py god function 290 lines WI-4b: dispatcher + 2 handlers
W4 WARNING deps/cli.py mixed data/rendering WI-4c: separated resolution from display
W5 WARNING script_runner.py runtime dispatch 124 stmts WI-4d: per-runtime builders

Test results

  • 5,441 unit tests passing (26 new tests for decomposition coverage)
  • 160 benchmark tests (154 benchmarks + 6 scaling guards)
  • Zero regressions

Related issues

sergio-sisternes-epam pushed a commit to sergio-sisternes-epam/apm that referenced this pull request Apr 25, 2026
Extract pure git ref helpers into git_remote_ops.py and backend
download logic into download_strategies.py DownloadStrategyManager.
Backward-compat method stubs on GitHubPackageDownloader preserve
all existing import paths and test patch points.

Part of complexity audit PR microsoft#918.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
sergio-sisternes-epam pushed a commit to sergio-sisternes-epam/apm that referenced this pull request Apr 25, 2026
Split 555-line install() into thin dispatcher + _install_apm_packages()
+ _handle_mcp_install(). Extract _resolve_package_references() and
_check_package_conflicts() from _validate_and_add_packages_to_apm_yml().
Add InstallContext dataclass to bundle shared parameters.

Part of complexity audit PR microsoft#918.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
sergio-sisternes-epam pushed a commit to sergio-sisternes-epam/apm that referenced this pull request Apr 25, 2026
Add tests for commands.install.InstallContext dataclass construction
and _resolve_package_references() batch-duplicate mutation contract.

Part of complexity audit PR microsoft#918.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sergio-sisternes-epam sergio-sisternes-epam marked this pull request as ready for review April 25, 2026 10:55
Copilot AI review requested due to automatic review settings April 25, 2026 10:55
@sergio-sisternes-epam sergio-sisternes-epam added the panel-review Trigger the apm-review-panel gh-aw workflow label Apr 25, 2026
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

Refactor-focused PR that reduces complexity and improves performance across install/audit/deps/uninstall paths, while adding benchmarks and characterization/unit tests to guard behavior and scaling.

Changes:

  • Introduces thread-safe caching/singletons (Rich console, marketplace registry cache) and reduces redundant I/O (lockfile reuse, per-runtime registry reads).
  • Replaces several O(n^2) loops with indexed/O(n) approaches (uninstall reverse-dep traversal, discovery scanning strategy).
  • Adds substantial benchmark/scaling-guard coverage plus characterization/unit tests for refactored components.

Reviewed changes

Copilot reviewed 42 out of 42 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/unit/test_uninstall_engine_helpers.py Adds unit coverage for uninstall reverse-dependency children index helper.
tests/unit/test_transitive_mcp.py Updates tests to match MCPIntegrator logging/output behavior changes.
tests/unit/test_thread_safety.py Adds concurrency tests for console singleton and marketplace registry cache lock.
tests/unit/test_script_formatters.py Adds tests for ScriptExecutionFormatter Rich fallback branches and formatting.
tests/unit/test_registry_integration.py Adds tests ensuring MCP registry runtime lookups are cached per runtime.
tests/unit/test_mcp_integrator_remove_stale.py Adds characterization coverage for MCPIntegrator.remove_stale() argument combinations.
tests/unit/test_mcp_integrator_coverage.py Adds coverage tests for MCPIntegrator branches + NullCommandLogger surface.
tests/unit/test_mcp_integrator_characterisation.py Adds install() characterization tests across logger/runtime/flags combinations.
tests/unit/test_console_utils.py Resets console singleton between tests to avoid cross-test coupling.
tests/unit/install/test_architecture_invariants.py Updates LOC budget gate for install.py after decomposition.
tests/unit/compilation/test_agents_compiler_coverage.py Adds test for constitution injection failure being swallowed + debug-logged.
tests/unit/commands/test_install_resolve_refs.py Adds tests for in-place identity-set mutation contract in resolver helper.
tests/unit/commands/test_install_context.py Adds structural tests for commands.install.InstallContext dataclass.
tests/benchmarks/test_scaling_guards.py Adds non-benchmark scaling guards to detect complexity regressions in CI.
tests/benchmarks/test_install_hot_paths.py Adds benchmark suite for install hot paths (hashing, lockfile ops, flattening, etc.).
tests/benchmarks/test_git_and_compiler_benchmarks.py Adds benchmarks for git ref parsing/sorting, compiler analysis, and MCP transitive collection.
tests/benchmarks/test_compilation_hot_paths.py Adds compilation/integration benchmarks (hashing, partitioning, link rewrite, lockfile RTT).
tests/benchmarks/test_audit_benchmarks.py Adds audit-focused benchmarks for discovery, uninstall indexing, registry caching, console singleton, etc.
src/apm_cli/utils/console.py Implements thread-safe Rich Console singleton + test-only reset hook.
src/apm_cli/registry/operations.py Caches installed server IDs per runtime to avoid O(servers*runtimes) config reads.
src/apm_cli/primitives/discovery.py Refactors primitive discovery to single os.walk pass with pattern matching helpers.
src/apm_cli/policy/discovery.py Improves exception hygiene by logging token manager failure at debug level.
src/apm_cli/output/script_formatters.py Replaces bare except: with except Exception: in Rich fallback blocks.
src/apm_cli/output/formatters.py Replaces bare except: with except Exception: in multiple formatting fallbacks.
src/apm_cli/models/dependency/reference.py Splits parsing logic into smaller helpers; centralizes validation/final field extraction.
src/apm_cli/models/apm_package.py Deduplicates dependency parsing for dependencies/devDependencies via helper.
src/apm_cli/marketplace/registry.py Adds lock around registry cache to make load/save/invalidate thread-safe.
src/apm_cli/integration/mcp_integrator.py Introduces NullCommandLogger default and removes many logger=None forks.
src/apm_cli/install/pipeline.py Threads early lockfile through InstallContext to avoid re-reading.
src/apm_cli/install/phases/resolve.py Uses cached early lockfile when available and improves verbose lockfile logging.
src/apm_cli/install/context.py Adds early_lockfile field to install pipeline context.
src/apm_cli/deps/git_remote_ops.py Extracts pure helpers for parsing/sorting git ls-remote output.
src/apm_cli/core/script_runner.py Decomposes runtime command transformation and avoids redundant apm_modules scans.
src/apm_cli/core/null_logger.py Adds NullCommandLogger (null-object) to centralize default logging behavior.
src/apm_cli/compilation/agents_compiler.py Adds debug logging for previously silent exception paths.
src/apm_cli/commands/uninstall/engine.py Adds O(n) children index and uses it to avoid repeated full scans.
src/apm_cli/commands/deps/cli.py Splits data resolution from rendering; extracts helpers for tree formatting.
src/apm_cli/commands/audit.py Decomposes audit command into mode handlers with shared config dataclass.
CHANGELOG.md Adds Unreleased entries describing refactors, benchmarks, and exception hygiene updates.

Comment thread src/apm_cli/integration/mcp_integrator.py
Comment thread src/apm_cli/integration/mcp_integrator.py
Comment thread tests/unit/commands/test_install_context.py Outdated
Comment thread CHANGELOG.md Outdated
@github-actions
Copy link
Copy Markdown

APM Review Panel Verdict

Disposition: REQUEST_CHANGES (two concrete pre-merge fixes; overall shape is excellent)


Per-persona findings

Python Architect:

This is a major architectural change: three new modules introduced (download_strategies.py, git_remote_ops.py, null_logger.py), a class hierarchy restructured around a delegation pattern, and two new parameter-bundle dataclasses (InstallContext in commands/install.py, _AuditConfig in audit.py). Before/After diagrams follow.

Before -- WI-2 class layout (monolith)

classDiagram
    direction LR
    class GitHubPackageDownloader {
        <<Monolith>>
        +resilient_get()
        +build_repo_url()
        +download_file()
        +download_artifactory_archive()
        +parse_ls_remote_output()
        +semver_sort_key()
        +sort_remote_refs()
        +github_token str
        +ado_token str
        +auth_resolver AuthResolver
    }
    class AuthResolver {
        <<Strategy>>
        +resolve_for_dep(dep_ref) AuthContext
    }
    GitHubPackageDownloader ..> AuthResolver : reads
    note for GitHubPackageDownloader "All download, URL-build, and ref-sort\nlogic inlined -- 1000+ line module"
Loading

After -- WI-2 class layout (decomposed)

classDiagram
    direction LR
    class GitHubPackageDownloader {
        <<Facade>>
        +github_token str
        +ado_token str
        +auth_resolver AuthResolver
        +_strategies DownloadStrategyManager
        +_resilient_get() -- stub
        +build_repo_url() -- stub
        +download_artifactory_archive() -- stub
    }
    class DownloadStrategyManager {
        <<ConcreteStrategy>>
        +_host GitHubPackageDownloader
        +resilient_get()
        +build_repo_url()
        +download_file()
        +download_artifactory_archive()
    }
    class git_remote_ops {
        <<Pure>>
        +parse_ls_remote_output()
        +semver_sort_key()
        +sort_remote_refs()
    }
    class NullCommandLogger {
        <<NullObject>>
        +verbose = False
        +warning()
        +progress()
        +error()
        +success()
    }
    class InstallContextCmds {
        <<ParameterBundle>>
        +scope
        +logger
        +force bool
        +dry_run bool
    }
    class _AuditConfig {
        <<ValueObject>>
        +project_root Path
        +logger CommandLogger
        +verbose bool
        +output_format str
    }
    GitHubPackageDownloader *-- DownloadStrategyManager : owns
    DownloadStrategyManager ..> GitHubPackageDownloader : back-ref (_host)
    GitHubPackageDownloader ..> git_remote_ops : imports
    class GitHubPackageDownloader:::touched
    class DownloadStrategyManager:::touched
    class git_remote_ops:::touched
    class NullCommandLogger:::touched
    class InstallContextCmds:::touched
    class _AuditConfig:::touched
    classDef touched fill:#fff3b0,stroke:#d47600
    note for DownloadStrategyManager "Bidirectional coupling via _host --\nsee Required Actions"
Loading

Before -- WI-2/WI-3 execution flow

flowchart TD
    A["apm install (CLI)"] --> B["install() -- 555 lines"]
    B --> C["_validate_and_add_packages_to_apm_yml -- 290 lines"]
    C --> D["[I/O] load_yaml(apm.yml)"]
    D --> E["inline duplicate detection loop"]
    E --> F["inline package validation loop"]
    F --> G["[I/O] dump_yaml(apm.yml)"]
    G --> H["run_install_pipeline()"]
    H --> I["resolve.run() -- [I/O] LockFile.read() x2"]
Loading

After -- WI-2/WI-3 execution flow

flowchart TD
    A["apm install (CLI)"] --> B["install()"]
    B --> C["InstallContext -- commands/install.py"]
    C --> D["_validate_and_add_packages_to_apm_yml"]
    D --> D1["_check_package_conflicts()"]
    D --> D2["_resolve_package_references()"]
    D --> D3["_merge_packages_into_yml()"]
    D3 --> E["[I/O] dump_yaml(apm.yml)"]
    E --> F["run_install_pipeline()"]
    F --> G["install/context.py:InstallContext -- early_lockfile cached"]
    G --> H["resolve.run() -- [I/O] LockFile.read() x1 (cache hit skips)"]
Loading

Design patterns

  • Used in this PR:
    • Facade -- GitHubPackageDownloader becomes a thin facade with backward-compat stubs delegating to DownloadStrategyManager
    • Null Object -- NullCommandLogger eliminates 32 if logger: forks in MCPIntegrator
    • Parameter Bundle (DTO) -- InstallContext (commands) and _AuditConfig reduce multi-argument function signatures to a single config object
    • Pure Module -- git_remote_ops.py is stateless helpers with no side effects (<<Pure>> stereotype)
  • Pragmatic suggestion: DownloadStrategyManager._host is a bidirectional back-reference that keeps orchestrator and strategy coupled at the instance level. A thin _DownloaderConfig dataclass holding only github_token, ado_token, auth_resolver, and registry_config would sever the cycle without adding abstraction depth. At this PR's current size this is optional, not required.

Architecture issue -- dual InstallContext name: src/apm_cli/commands/install.py:161 defines InstallContext; src/apm_cli/install/context.py:116 also defines InstallContext. Both are used in the install pathway. The commands-layer one should be renamed (e.g. _AddPackagesParams) to prevent future import shadowing and make grep results unambiguous.


CLI Logging Expert:

NullCommandLogger is a clean Null Object that eliminates the 32 if logger: forks in MCPIntegrator. The methods it implements (warning, progress, error, success, verbose_detail, tree_item, package_inline_warning) match exactly the methods MCPIntegrator calls on its logger parameter -- no AttributeError risk in current usage.

One cosmetic bug: _merge_packages_into_yml (line 463) sets dep_label = "devDependencies" if dev else "apm.yml". The non-dev branch says "apm.yml" where it should say "dependencies". The verbose_detail message then reads "Added foo to apm.yml" instead of "Added foo to dependencies". Low severity (verbose-only), but misleading.

Audit command decomposition routes through CommandLogger correctly -- _audit_ci_gate and _audit_content_scan both receive the logger via cfg.logger and call .error(), .warning(), .success(), .progress() exclusively. No rogue _rich_* calls in command functions. Good.


DevX UX Expert:

This is a pure internal refactoring. No command surface, flags, help text, or exit codes are changed. The apm install, apm audit, apm update user experience is identical before and after.

The early_lockfile caching on InstallContext eliminates a redundant lockfile read -- users won't notice, but CI pipelines on large monorepos will see a small improvement.

No documentation changes are needed (no command surface delta). cli-commands.md and quick-start.md are unaffected.

One ergonomic note echoing the logging finding: a verbose user running apm install --verbose mypackage would see "Added mypackage to apm.yml" (bug) rather than "Added mypackage to dependencies". Trivial, but worth fixing before it lands.


Supply Chain Security Expert:

Reviewed against all seven threat-model categories. Key findings:

  1. Zip path traversal guard preserved (threat 7): download_artifactory_archive moved to DownloadStrategyManager with the containment guard intact: if not dest.resolve().is_relative_to(target_path.resolve()). No regression.

  2. Bearer token URL non-embedding preserved (threat 6): build_repo_url with auth_scheme == "bearer" still passes token=None to build_ado_https_clone_url and injects credentials via GIT_CONFIG env vars. Token never appears in a URL or log.

  3. _strip_extended_prefix removal (src/apm_cli/utils/path_security.py): This was the Windows PyInstaller \\?\ prefix fix (Windows: _get_cache_dir flakes on tmpdir 8.3 short names (PathTraversalError) #886). Removing it is safe because the project targets Python 3.12 where Path.resolve() handles extended-length paths consistently. However, the removal carries no explanatory comment -- a future reader (or a contributor running Python 3.10/3.11) will not know why the guard was dropped. A one-line comment citing Windows: _get_cache_dir flakes on tmpdir 8.3 short names (PathTraversalError) #886 and Python 3.12 is a required action.

  4. subprocess_env.py deletion: The module is fully unreferenced (confirmed: grep -r external_process_env src/ finds no hits). Deletion is clean. The PyInstaller LD_LIBRARY_PATH leakage issues ([BUG] Fedora: brew apm fails on Git clone due to bundled OpenSSL mismatch #462, [BUG] apm update fails #894) were addressed via a different mechanism upstream. No regression.

  5. _resolve_package_references extraction: The insecure-URL guards and local-at-user-scope rejection appear correctly preserved in the extracted helper. No paths were silently removed.


Auth Expert:

Activated (fast-path trigger: src/apm_cli/deps/github_downloader.py changed).

DownloadStrategyManager.build_repo_url reads tokens via self._host.github_token and self._host.ado_token -- these are AuthResolver-resolved instance fields on the owning GitHubPackageDownloader, not direct os.getenv() calls. Token precedence chain is unchanged: all resolution still flows through AuthResolver before reaching the downloader instance.

The token == "" sentinel case (explicit empty string to suppress per-instance token for plain HTTPS/SSH transport attempts) is correctly handled in DownloadStrategyManager.build_repo_url at lines that set github_token = "" and ado_token = "". This is a deliberate transport-selector escape hatch and is preserved faithfully.

ADO bearer scheme: auth_scheme == "bearer" branch passes token=None to build_ado_https_clone_url(). Bearer tokens are injected separately via GIT_CONFIG env vars (not embedded in URL). This is correct per the auth architecture.

get_artifactory_headers() delegation: The backward-compat stub on GitHubPackageDownloader delegates to self._strategies.get_artifactory_headers(). The actual header construction uses self._host.registry_config or falls back to self._host.artifactory_token -- both read via the back-reference, same fields, same fallback chain. No credential-handling regression.

No new os.getenv token reads introduced. AuthResolver remains the sole credential source. No regression to AuthResolver precedence, host classification, or credential leakage surface.


OSS Growth Hacker:

No user-facing feature lands with this PR -- it is a pure engineering investment. Three growth implications worth noting:

  1. Contributor funnel improvement (side-channel to CEO): Splitting github_downloader.py from ~1000 lines to three focused modules lowers the cognitive barrier for first-time contributors. The "god file" problem is a known contributor churn driver in OSS. This is a real, compounding gain.

  2. Benchmark suite as enterprise trust signal: 145+ new benchmark tests covering hot paths is a credibility signal for enterprise evaluators. "APM instruments its own performance" is a one-liner that maps cleanly to the "production-grade package manager" positioning.

  3. No conversion surface touched: README.md, quickstart, and first-run flow are unaffected. The _strip_extended_prefix removal is a potential regression on non-Python-3.12 Windows builds if a user runs APM from source under an older Python -- worth flagging to the CEO but not a launch-narrative risk for binary users.

Side-channel to CEO: the dual InstallContext naming is a future contributor confusion risk that could generate low-quality issues or incorrect PRs from contributors who import the wrong class. Worth fixing before the branch merges.


CEO arbitration

Specialists are broadly aligned: the engineering quality of this refactoring is high, patterns are correctly applied, and no auth or supply-chain regressions are introduced. Two issues rise to required-action level. First, _strip_extended_prefix was a deliberate security guard for PyInstaller + Windows; removing it without a comment creates a maintenance trap for any contributor who hits the Windows edge case, runs git bisect, and finds the guard gone with no explanation -- a one-line comment citing #886 costs nothing and prevents future confusion. Second, the duplicate InstallContext name across commands/install.py and install/context.py will produce hard-to-diagnose shadowing bugs once external contributors import from either module: rename the commands-layer class now while the PR is still open. The dep_label cosmetic bug ("apm.yml" vs "dependencies" in a verbose_detail message) is a required fix to keep verbose output trustworthy. These three changes are small, concrete, and unambiguous. The benchmark suite, NullCommandLogger, and O(n^2)->O(n) performance work are all ratified as mergeable; the code quality of WI-1 through WI-3 is a net positive for the project. Disposition: REQUEST_CHANGES on the three items below.


Required actions before merge

  1. src/apm_cli/utils/path_security.py -- add a comment explaining _strip_extended_prefix removal. One line is enough: # _strip_extended_prefix removed: Python 3.12 Path.resolve() handles \\?\ prefixes correctly (see #886). Without it, a future contributor or security auditor will re-add the check (or question the omission in review) creating unnecessary churn.

  2. src/apm_cli/commands/install.py:161 -- rename InstallContext to _AddPackagesParams (or similar). The install pathway now contains two distinct InstallContext dataclasses (commands/install.py:161 and install/context.py:116). Both are used in the same feature, both carry "install context" semantics, and both will appear in search results and IDE autocomplete. Rename the commands-layer one to remove ambiguity before this lands.

  3. src/apm_cli/commands/install.py:463 -- fix dep_label value. Change "devDependencies" if dev else "apm.yml" to "devDependencies" if dev else "dependencies". The verbose_detail message currently tells users their package was added "to apm.yml" instead of "to dependencies".


Optional follow-ups

  • DownloadStrategyManager._host back-reference: consider introducing a _DownloaderConfig dataclass holding only github_token, ado_token, auth_resolver, and registry_config -- injected into both GitHubPackageDownloader and DownloadStrategyManager to break the bidirectional coupling. Not required at this scope but would simplify future unit-testing of the strategy manager in isolation.
  • NullCommandLogger docstring: narrow "drop-in replacement for CommandLogger" to "drop-in for contexts where only warning/progress/error/success are called (e.g. MCPIntegrator)". The current docstring overclaims: CommandLogger has additional phase methods (lockfile_entry, validation_start, etc.) that NullCommandLogger does not implement. Callers outside MCPIntegrator that use the full InstallLogger interface would get AttributeError.
  • Add a dated entry to WIP/growth-strategy.md (maintainer-local, gitignored): "WI-1/2/3 complexity audit reduces contributor onboarding friction -- link the module split in next release notes as evidence of engineering maturity."

Generated by PR Review Panel for issue #918 · ● 1M ·

sergio-sisternes-epam pushed a commit to sergio-sisternes-epam/apm that referenced this pull request Apr 25, 2026
- Route user-visible MCP messages through logger.progress() instead of
  verbose_detail() so NullCommandLogger preserves output
- Fix trivially-passing frozen assertion in test_install_context.py
- Add missing (microsoft#918) PR references to CHANGELOG entries

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sergio-sisternes-epam sergio-sisternes-epam force-pushed the audit/complexity-review-worktree branch from 0ed31e7 to a1e68a7 Compare April 27, 2026 08:20
sergio-sisternes-epam pushed a commit to sergio-sisternes-epam/apm that referenced this pull request Apr 27, 2026
Extract pure git ref helpers into git_remote_ops.py and backend
download logic into download_strategies.py DownloadStrategyManager.
Backward-compat method stubs on GitHubPackageDownloader preserve
all existing import paths and test patch points.

Part of complexity audit PR microsoft#918.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
sergio-sisternes-epam pushed a commit to sergio-sisternes-epam/apm that referenced this pull request Apr 27, 2026
Split 555-line install() into thin dispatcher + _install_apm_packages()
+ _handle_mcp_install(). Extract _resolve_package_references() and
_check_package_conflicts() from _validate_and_add_packages_to_apm_yml().
Add InstallContext dataclass to bundle shared parameters.

Part of complexity audit PR microsoft#918.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
sergio-sisternes-epam pushed a commit to sergio-sisternes-epam/apm that referenced this pull request Apr 27, 2026
Add tests for commands.install.InstallContext dataclass construction
and _resolve_package_references() batch-duplicate mutation contract.

Part of complexity audit PR microsoft#918.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
sergio-sisternes-epam pushed a commit to sergio-sisternes-epam/apm that referenced this pull request Apr 27, 2026
- Route user-visible MCP messages through logger.progress() instead of
  verbose_detail() so NullCommandLogger preserves output
- Fix trivially-passing frozen assertion in test_install_context.py
- Add missing (microsoft#918) PR references to CHANGELOG entries

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
sergio-sisternes-epam pushed a commit to sergio-sisternes-epam/apm that referenced this pull request Apr 27, 2026
Extract pure git ref helpers into git_remote_ops.py and backend
download logic into download_strategies.py DownloadStrategyManager.
Backward-compat method stubs on GitHubPackageDownloader preserve
all existing import paths and test patch points.

Part of complexity audit PR microsoft#918.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
sergio-sisternes-epam pushed a commit to sergio-sisternes-epam/apm that referenced this pull request Apr 27, 2026
Split 555-line install() into thin dispatcher + _install_apm_packages()
+ _handle_mcp_install(). Extract _resolve_package_references() and
_check_package_conflicts() from _validate_and_add_packages_to_apm_yml().
Add InstallContext dataclass to bundle shared parameters.

Part of complexity audit PR microsoft#918.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
sergio-sisternes-epam pushed a commit to sergio-sisternes-epam/apm that referenced this pull request Apr 27, 2026
Add tests for commands.install.InstallContext dataclass construction
and _resolve_package_references() batch-duplicate mutation contract.

Part of complexity audit PR microsoft#918.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
sergio-sisternes-epam pushed a commit to sergio-sisternes-epam/apm that referenced this pull request Apr 27, 2026
- Route user-visible MCP messages through logger.progress() instead of
  verbose_detail() so NullCommandLogger preserves output
- Fix trivially-passing frozen assertion in test_install_context.py
- Add missing (microsoft#918) PR references to CHANGELOG entries

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sergio-sisternes-epam sergio-sisternes-epam force-pushed the audit/complexity-review-worktree branch from a1e68a7 to 56c4e2f Compare April 27, 2026 11:51
@sergio-sisternes-epam sergio-sisternes-epam added panel-review Trigger the apm-review-panel gh-aw workflow and removed panel-review Trigger the apm-review-panel gh-aw workflow labels Apr 27, 2026
@github-actions
Copy link
Copy Markdown

APM Review Panel Verdict

Disposition: APPROVE (with two pre-merge fixes)


Per-persona findings

Python Architect:

This is a major architectural refactor touching 43 files. Because it introduces new module boundaries, a new delegation pattern, and new dataclass abstractions, Before/After diagrams are warranted for the two biggest structural changes.

Before -- github_downloader.py as monolith + null-logger forks:

classDiagram
    direction LR
    class GitHubPackageDownloader {
        <<Orchestrator>>
        +auth_resolver AuthResolver
        +registry_config RegistryConfig
        +_resilient_get() Response
        +_build_repo_url() str
        +_get_artifactory_headers() dict
        +_download_artifactory_archive()
        +_download_file_from_artifactory()
        +_parse_ls_remote_output()
        +_sort_remote_refs()
    }
    class MCPIntegrator {
        +collect_transitive(logger=None)
        +install(logger=None)
        +remove_stale(logger=None)
    }
    class CommandLogger {
        <<Base>>
        +verbose bool
        +start()
        +progress()
        +dry_run_notice()
    }
    MCPIntegrator ..> CommandLogger : if logger: guard (32x)
    MCPIntegrator ..> GitHubPackageDownloader : uses
Loading

After -- decomposed with delegation and null-object:

classDiagram
    direction LR
    class GitHubPackageDownloader {
        <<Orchestrator>>
        +auth_resolver AuthResolver
        +registry_config RegistryConfig
        +_strategies DownloadStrategyManager
        +_resilient_get() Response
        +_build_repo_url() str
        +_get_artifactory_headers() dict
    }
    class DownloadStrategyManager {
        <<Delegate>>
        +_host GitHubPackageDownloader
        +resilient_get() Response
        +build_repo_url() str
        +get_artifactory_headers() dict
        +download_artifactory_archive()
        +download_file_from_artifactory()
    }
    class NullCommandLogger {
        <<NullObject_partial>>
        +verbose bool
        +start()
        +progress()
        +success()
        +warning()
        +error()
        +tree_item()
    }
    class CommandLogger {
        <<Base>>
        +verbose bool
        +start()
        +progress()
        +dry_run_notice()
        +validation_start()
        +validation_fail()
    }
    class InstallContext {
        <<ParameterBundle>>
        +scope InstallScope
        +logger InstallLogger
        +auth_resolver AuthResolver
        +verbose bool
        +force bool
    }
    class _AuditConfig {
        <<ParameterBundle, frozen>>
        +project_root Path
        +logger CommandLogger
        +verbose bool
        +output_format str
    }
    class MCPIntegrator {
        +collect_transitive(logger=NullCommandLogger)
        +install(logger=NullCommandLogger)
        +remove_stale(logger=NullCommandLogger)
    }
    class GitHubPackageDownloader:::touched
    class DownloadStrategyManager:::touched
    class NullCommandLogger:::touched
    class InstallContext:::touched
    class _AuditConfig:::touched
    class MCPIntegrator:::touched
    GitHubPackageDownloader *-- DownloadStrategyManager : delegates to
    DownloadStrategyManager ..> GitHubPackageDownloader : back-ref host
    MCPIntegrator ..> NullCommandLogger : default logger
    MCPIntegrator ..> CommandLogger : uses
    classDef touched fill:#fff3b0,stroke:#d47600
Loading

Execution flow (After -- key paths changed by this PR):

flowchart TD
    A["MCPIntegrator.install(logger=None)"] --> B{"logger is None?"}
    B -- Yes --> C["logger = NullCommandLogger()\ncalls _rich_* directly"]
    B -- No --> D["use provided CommandLogger"]
    C --> E["MCPIntegrator proceeds\nno if-logger: guards needed"]
    D --> E
    E --> F["GitHubPackageDownloader._resilient_get(url, headers)"]
    F --> G["DownloadStrategyManager.resilient_get()\n[NET] HTTP GET + rate-limit retry"]
    G --> H{"HTTP 429/503 or 403+RateLimit?"}
    H -- Yes --> I["sleep Retry-After or exp-backoff\nmax 3 attempts"]
    I --> G
    H -- No --> J["return Response"]
    J --> K{"Artifactory archive needed?"}
    K -- Yes --> L["DownloadStrategyManager.download_artifactory_archive()\n[NET][FS] zip fetch"]
    L --> M{"zip entry dest.resolve()\noutside target_path?"}
    M -- Yes --> N["skip entry -- CWE-22 guard preserved"]
    M -- No --> O["[FS] extract member to target_path"]
    K -- No --> P["return bytes"]

    A2["uninstall BFS orphan detection"] --> B2["_build_children_index(lockfile)\n[LOCK] single O(n) pass"]
    B2 --> C2["children dict: parent_url -> list of child deps"]
    C2 --> D2["BFS pop parent_url\nchildren_index.get(parent_url,[]) O(1)"]
    D2 --> E2["collect orphan keys"]

    A3["_scan_patterns(base_dir, patterns)"] --> B3["os.walk(base_str, followlinks=False)\nsingle pass -- no symlink follow"]
    B3 --> C3["_matches_any_pattern(rel_path, all_patterns)"]
    C3 -- match --> D3["parse_primitive_file(file_path)\n[FS] read + parse"]
    C3 -- no match --> B3
    D3 --> E3["collection.add_primitive()"]
Loading

Design patterns:

Pattern Location Notes
Delegate (Facade) GitHubPackageDownloader -> DownloadStrategyManager Circular back-ref via _host; more Facade than Strategy despite naming
Null Object (partial) NullCommandLogger in MCPIntegrator Produces output via _rich_*; NOT a true silent null -- see CLI Logging finding
Parameter Bundle (frozen) _AuditConfig Correct use of frozen dataclass
Parameter Bundle (mutable) InstallContext Not frozen; only_packages set post-construction -- intentional
Singleton + DCLP _get_console() in console.py Correctly holds lock across check and set
Incorrect DCLP _load() in marketplace/registry.py Bug: lock released between check and set -- see Required Actions
Reverse-Index BFS _build_children_index() in uninstall O(n^2) -> O(n) -- correct and well-tested
Single-Pass Walk _scan_patterns() in primitives/discovery.py O(patterns) filesystem traversals -> O(1); followlinks=False is a security improvement over the previous glob.glob

One structural concern: DownloadStrategyManager is named a "strategy manager" but holds a circular back-reference to its owner (self._host). This is a Facade/Delegate, not a GoF Strategy (strategies should be interchangeable; this one is tightly coupled to one owner). The pattern is functionally correct, but the name may confuse future contributors. No block, but tracked in Optional Follow-ups.


CLI Logging Expert:

The NullCommandLogger is the main output architecture change. Three findings:

  1. Misnaming and semantics: NullCommandLogger is not null -- start(), progress(), success(), warning(), error(), and tree_item() all call _rich_* helpers and produce visible console output. Only verbose_detail() and package_inline_warning() are silently discarded. The name implies silence; the behavior is not silent. Code that previously used logger=None to suppress all MCPIntegrator output will now see console output it did not ask for. This is almost certainly an improvement in the MCPIntegrator CLI context, but any programmatic/test caller that relied on logger=None producing no output is affected.

  2. Interface incompleteness: NullCommandLogger is missing dry_run_notice, validation_start, validation_fail, and all InstallLogger-specific methods. This is fine because it is only wired into MCPIntegrator which does not call those methods. The concern is forward-looking: if NullCommandLogger is reused elsewhere (its module placement in core/ invites reuse), callers expecting full CommandLogger compatibility will get AttributeError at runtime. The class docstring should explicitly list the subset it implements and state it is NOT a drop-in for InstallLogger.

  3. agents_compiler.py and discovery.py debug logging: Bare except: -> except Exception as exc: _logger.debug(...) is correct. These use Python's logging module (not CommandLogger), which is appropriate for library-level internal traces. The debug message in discovery.py:_get_token_for_host() does not include the token value (only the host and exception message), so no credential leakage risk.


DevX UX Expert:

No CLI surface changes -- zero new commands, flags, or help text. Every decomposition is internal. The apm audit, apm install, and apm uninstall command interfaces are identical to pre-PR. All existing error messages are preserved verbatim through the extraction helpers (_merge_packages_into_yml, _validate_and_add_packages_to_apm_yml). No doc sync required (CHANGELOG updated; no new user-facing behavior).

One ergonomic observation: the behavior change from logger=None -> NullCommandLogger() as the MCPIntegrator default means programmatic callers get console output when they did not ask for it. Not a blocker -- the MCPIntegrator is primarily used in CLI flows -- but worth noting in release comms for any library users.


Supply Chain Security Expert:

  1. Zip traversal guard preserved: The CWE-22 path traversal check (dest.resolve().is_relative_to(target_path.resolve())) is present in DownloadStrategyManager.download_artifactory_archive() (diff lines 3114-3120). The move from github_downloader.py preserved the guard. No regression. ✓

  2. _scan_patterns symlink safety improvement: The refactored os.walk(base_str, followlinks=False) is strictly safer than the previous glob.glob(str(base_dir / pattern), recursive=True), which follows symlinks by default. This is a net security improvement. ✓

  3. DownloadStrategyManager auth routing: self._host.auth_resolver is the only token source used in download_strategies.py; no new direct os.getenv() calls for token values. AuthResolver precedence unchanged. ✓

  4. Incorrect DCLP in marketplace/registry.py:_load(): The lock is released between the cache-miss check and the cache assignment (disk I/O runs outside the lock). Two concurrent threads can both observe _registry_cache is None, both read from disk, and both write. This is idempotent for read-only local config but is architecturally incorrect. If _invalidate_cache() or _save() races with _load(), the cache could reflect a stale pre-save state. This is Required Action Why do we need a GitHub token? #1. (Low exploitation risk; not a security vulnerability in the threat-model sense, but incorrect concurrent code in a correctness PR should not be merged.)

  5. _resolve_package_references mutates existing_identities: The mutation contract is documented in the docstring. Acceptable.


Auth Expert:

Activated -- src/apm_cli/deps/github_downloader.py is on the fast-path trigger list.

  1. _build_repo_url and _resilient_get moved to DownloadStrategyManager: Both are backward-compat stubs in github_downloader.py that delegate to self._strategies. The actual implementations in DownloadStrategyManager access credentials exclusively via self._host.auth_resolver and the git environment setup (self._host._setup_git_environment()). Token precedence chain (GITHUB_APM_PAT_{ORG} -> GITHUB_APM_PAT -> GITHUB_TOKEN -> GH_TOKEN -> git credential fill) is unchanged. ✓

  2. _get_artifactory_headers stub: Delegates to DownloadStrategyManager.get_artifactory_headers(), which reads from self._host.registry_config. Legacy artifactory_token fallback path preserved. ✓

  3. policy/discovery.py:_get_token_for_host() debug logging: logger.debug("Token manager failed for %s: %s", host, exc) uses the Python logging module at DEBUG level. The exc argument is a TokenManager exception message, not a token value. No credential leakage path. The fallback to os.environ.get("GITHUB_TOKEN") is unchanged. ✓

  4. No direct os.getenv() for tokens in new code: Spot-checked download_strategies.py, git_remote_ops.py, null_logger.py, context.py. None introduce raw env-var token reads. ✓

  5. Testability note (not a security issue): DownloadStrategyManager cannot be instantiated without a full GitHubPackageDownloader mock due to the circular back-reference. Auth behavior tests for the strategy manager require mocking the host. The existing test suite patches _resilient_get on the orchestrator (backward-compat stub), so existing tests are unaffected.

Auth Expert conclusion: No regression to AuthResolver precedence, host classification, token scoping, or credential fallback semantics.


OSS Growth Hacker:

Side-channel note to CEO: This PR is a high-credibility signal for the OSS community. The 160 benchmark tests, O(n^2)->O(n) algorithmic proof in uninstall, and 5,441 passing tests with zero regressions are exactly the kind of evidence that turns observers into adopters. Recommended beat for the next release post: "APM performance benchmarks: we now catch O(n^2) regressions in CI before they ship." The NullCommandLogger elimination of 32 conditional forks is a good engineering story for potential contributors -- it shows the codebase is actively improving its internal quality, not just adding features.

No conversion surface impacts (README, quickstart, first-run flow unchanged). The InstallContext dataclass and function extractions lower the barrier for new contributors to understand the install pipeline -- positive for the OSS contribution funnel.


CEO arbitration

This PR is a well-executed complexity audit. All specialists found the decompositions sound, algorithmic improvements correct, and the test coverage comprehensive. There are no strategic disagreements to arbitrate. The two pre-merge fixes identified (incorrect DCLP in registry, NullCommandLogger naming/documentation) are both correctness issues in a PR specifically about correctness -- they must land before merge. The Growth Hacker's call to mine this for a release beat is ratified: "APM now ships performance scaling guards in CI" is a story worth telling. No breaking changes are introduced; CHANGELOG is complete.


Required actions before merge

  1. Fix _load() in src/apm_cli/marketplace/registry.py (incorrect DCLP): The lock is released between the cache-miss check (line 49-51) and the cache assignment (line 68-69). Disk I/O runs outside the lock, allowing two concurrent threads to both miss the cache and both write. Use the same pattern as _get_console() -- hold the lock across the entire check + read + set operation:

    def _load() -> List[MarketplaceSource]:
        global _registry_cache
        with _registry_lock:
            if _registry_cache is not None:
                return list(_registry_cache)
            path = _ensure_file()
            try:
                with open(path, "r") as f:
                    data = json.load(f)
            except (json.JSONDecodeError, OSError) as exc:
                logger.warning("Failed to read %s: %s", path, exc)
                data = {"marketplaces": []}
            sources: List[MarketplaceSource] = []
            for entry in data.get("marketplaces", []):
                try:
                    sources.append(MarketplaceSource.from_dict(entry))
                except (KeyError, TypeError) as exc:
                    logger.debug("Skipping invalid marketplace entry: %s", exc)
            _registry_cache = sources
            return list(sources)
  2. Document NullCommandLogger's partial interface in src/apm_cli/core/null_logger.py: The class docstring must state (a) which CommandLogger methods it does NOT implement (dry_run_notice, validation_start, validation_fail, and InstallLogger-specific methods), (b) that it produces visible console output via _rich_* (it is NOT a silent null), and (c) that it is scoped to MCPIntegrator and is NOT a drop-in for InstallLogger or CommandLogger in command functions. Rename is optional but ConsoleFallbackLogger would be more accurate.


Optional follow-ups

  • Rename DownloadStrategyManager -> DownloadDelegate or DownloadBackend to better reflect that it is a Facade/Delegate (not a switchable Strategy) and document the circular back-reference as a known trade-off (issue for future decoupling).
  • The seen set in _scan_patterns() (primitives/discovery.py) is defensive but unnecessary since os.walk(..., followlinks=False) cannot revisit the same inode. Remove for clarity.
  • Benchmark tests that use time.time() wall-clock assertions (e.g., assert elapsed < 1.0) may be flaky under CI load. Consider replacing wall-clock guards with the relative-ratio pattern already used in the scaling guards, or move them to the ci-runtime.yml nightly suite where dedicated resources reduce noise.
  • Follow-up issues Audit: auth.py exception cascade -- 15 clauses need security-aware review #935 (auth.py exception cascade) and Security: policy_checks.py silently bypasses enforcement on malformed YAML #936 (policy_checks.py silent bypass) are correctly scoped out of this PR and should be prioritized in the next sprint.

Generated by PR Review Panel for issue #918 · ● 1.5M ·

sergio-sisternes-epam pushed a commit to sergio-sisternes-epam/apm that referenced this pull request Apr 27, 2026
Required fixes:
- Fix incorrect DCLP in marketplace registry _load() -- hold lock
  across full check+read+set to prevent race condition
- Document NullCommandLogger partial interface and visible-output
  semantics in docstring

Optional follow-ups (implemented to minimise tech debt):
- Rename DownloadStrategyManager to DownloadDelegate to reflect
  Facade/Delegate pattern (not GoF Strategy)
- Remove redundant seen set from _scan_patterns() discovery walk
- Replace wall-clock benchmark assertions with generous 5x ceilings
  to prevent flakiness on slow runners
- Update CHANGELOG with all panel fix entries

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sergio-sisternes-epam
Copy link
Copy Markdown
Collaborator Author

Panel Findings -- All Addressed in a748912

All 6 findings from the latest panel verdict have been implemented:

Required (2/2)

# Finding Resolution
R1 Incorrect DCLP in registry._load() Fixed -- lock now held across full check+read+set operation
R2 NullCommandLogger undocumented partial interface Fixed -- docstring clarifies unimplemented methods, visible output semantics, and MCPIntegrator scope

Optional (4/4)

# Finding Resolution
O1 DownloadStrategyManager naming Renamed to DownloadDelegate with Facade/Delegate documentation
O2 Redundant seen set in _scan_patterns() Removed -- os.walk(followlinks=False) prevents revisits
O3 Wall-clock benchmark assertions All thresholds multiplied by 5x (min 2.0s floor) to prevent flakiness
O4 CHANGELOG entries Added under [Unreleased] with (#918)

Validation: 5,833 unit tests + 154 benchmarks passing.

@sergio-sisternes-epam sergio-sisternes-epam added this to the 0.10.1 milestone Apr 27, 2026
Sergio Sisternes and others added 12 commits April 27, 2026 16:14
…s, redundant I/O

Phase 0: Cache early lockfile on InstallContext (2x reads -> 1x);
         extract _parse_dependency_dict() classmethod in APMPackage.
Phase 1: Thread-safety gate -- console singleton with double-checked
         locking; marketplace registry cache lock.
Phase 2: Uninstall engine reverse-dep index O(n^2) -> O(n) via
         _build_children_index() helper.
Phase 3: NullCommandLogger null-object pattern eliminates 32 conditional
         logger forks in MCPIntegrator (net -91 production lines).
Phase 6: Primitive discovery -- replace 9+ glob.glob() calls with single
         os.walk + fnmatch pass.
Registry: Pre-load installed IDs per runtime, reducing config reads from
          O(servers x runtimes) to O(runtimes).

54 new tests added (40 characterisation + 14 unit).
5,415 tests pass (baseline 5,361 + 54 new).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
16 @pytest.mark.benchmark tests in test_audit_benchmarks.py covering:
- Phase 0: dependency parsing dedup (from_apm_yml with 50/100/200 deps)
- Phase 2: children index build (50/200/500 deps + correctness)
- Phase 3: NullCommandLogger dispatch overhead (20k calls)
- Phase 6: primitive discovery (100/500 files + empty-match)
- Registry: config cache O(R) call-count verification
- Console: singleton performance + 50-thread concurrency

3 scaling-ratio guards in test_scaling_guards.py (run in default suite):
- Children index: O(n) scaling assertion (ratio < 25 for 10x input)
- Discovery scan: O(n) scaling assertion (ratio < 25 for 10x input)
- Console singleton: O(1) scaling assertion (ratio < 15 for 10x calls)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add 52 new benchmark tests and 2 scaling guards covering all critical
install and compilation hot paths:

P0 (install path):
- compute_package_hash throughput and determinism
- get_all_dependencies sort latency and repeated-call ceiling
- is_semantically_equivalent early-exit and full-scan
- flatten_dependencies with conflict resolution
- LockFile.to_yaml serialisation

P1 (compilation path):
- compute_deployed_hashes throughput and correctness
- ContextOptimizer.optimize_instruction_placement scaling
- UnifiedLinkResolver._rewrite_markdown_links latency
- BaseIntegrator.partition_managed_files routing
- LockFile round-trip (to_yaml + from_yaml) data preservation
- UnifiedLinkResolver.register_contexts throughput

Scaling guards (run in default test suite):
- compute_package_hash O(n) verification
- is_semantically_equivalent O(n) verification

Addresses testing-engineer review: console singleton teardown,
flaky timing comparison removal, threshold tightening, variable
rename, and conflict correctness assertion.

Total benchmark suite: 82 tests (77 benchmarks + 5 scaling guards)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add 77 new benchmark tests and 1 scaling guard covering hot paths
identified by Python Architect analysis:

P0 (superlinear risk):
- _match_double_star recursive glob matcher (O(m^k) worst case)
- ContentScanner.scan_text per-character Unicode scanning
- ContentScanner.strip_dangerous per-character content rewrite
- build_dependency_tree BFS traversal with per-node YAML parse

P1 (meaningful coverage gaps):
- _parse_ls_remote_output + _sort_remote_refs (semver sorting)
- DistributedAgentsCompiler.analyze_directory_structure
- MCPIntegrator.collect_transitive lock-file-guided scanning

Scaling guard:
- should_exclude depth scaling (sub-quadratic verification)

Addresses testing-engineer review: fast-path relative assertion,
redundant cache clear removal, all-tags sort invariant, docstring
clarity, test naming, and severity assertion strengthening.

Total benchmark suite: 160 tests (154 benchmarks + 6 scaling guards)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace 7 bare except: clauses that catch BaseException (including
KeyboardInterrupt and SystemExit) with except Exception: in formatters.py
and script_formatters.py. Add logger.debug() to 4 silent exception
handlers in discovery.py and agents_compiler.py to make credential
resolution and config loading failures visible with --verbose.

CEO-ratified findings B3 and B4 from Round 2 quality audit.

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

WI-4a: reference.py -- _parse_standard_url (110->22 stmts) and parse
(63->38 stmts) split into 6 focused helpers by parsing phase.

WI-4b: audit.py -- audit() (290 lines, 13 params) split into thin
dispatcher (18 stmts) + _audit_ci_gate (49) + _audit_content_scan (67)
with shared _AuditConfig dataclass.

WI-4c: deps/cli.py -- _show_scope_deps (135->59 stmts) and tree
(115->65 stmts) split data resolution from rendering via
_resolve_scope_deps and _build_dep_tree helpers.

WI-4d: script_runner.py -- _transform_runtime_command (124->20 stmts)
split into per-runtime builder dispatch. Fixed double iterdir() walk
in _resolve_prompt_file via single-scan _collect_dependency_dirs.

All changes are internal refactors with no public API changes.
CEO-ratified findings W2, W3, W4, W5 from Round 2 quality audit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add 8 unit tests for ScriptExecutionFormatter (G1 gap: both Rich
fallback branches + happy paths for content preview, auto-discovery,
execution success/error, and script header formatting).

Add 1 test for CLAUDE.md constitution injection failure path (G2 gap:
verifies compilation succeeds and _logger.debug is called when
ConstitutionInjector.inject raises).

Update CHANGELOG with WI-4 god function decomposition entries.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Extract pure git ref helpers into git_remote_ops.py and backend
download logic into download_strategies.py DownloadStrategyManager.
Backward-compat method stubs on GitHubPackageDownloader preserve
all existing import paths and test patch points.

Part of complexity audit PR microsoft#918.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Split 555-line install() into thin dispatcher + _install_apm_packages()
+ _handle_mcp_install(). Extract _resolve_package_references() and
_check_package_conflicts() from _validate_and_add_packages_to_apm_yml().
Add InstallContext dataclass to bundle shared parameters.

Part of complexity audit PR microsoft#918.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add tests for commands.install.InstallContext dataclass construction
and _resolve_package_references() batch-duplicate mutation contract.

Part of complexity audit PR microsoft#918.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Route user-visible MCP messages through logger.progress() instead of
  verbose_detail() so NullCommandLogger preserves output
- Fix trivially-passing frozen assertion in test_install_context.py
- Add missing (microsoft#918) PR references to CHANGELOG entries

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Required fixes:
- Fix incorrect DCLP in marketplace registry _load() -- hold lock
  across full check+read+set to prevent race condition
- Document NullCommandLogger partial interface and visible-output
  semantics in docstring

Optional follow-ups (implemented to minimise tech debt):
- Rename DownloadStrategyManager to DownloadDelegate to reflect
  Facade/Delegate pattern (not GoF Strategy)
- Remove redundant seen set from _scan_patterns() discovery walk
- Replace wall-clock benchmark assertions with generous 5x ceilings
  to prevent flakiness on slow runners
- Update CHANGELOG with all panel fix entries

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sergio-sisternes-epam sergio-sisternes-epam force-pushed the audit/complexity-review-worktree branch from eb7f35a to 64cbd2a Compare April 27, 2026 15:16
@sergio-sisternes-epam sergio-sisternes-epam added this pull request to the merge queue Apr 28, 2026
Merged via the queue into microsoft:main with commit bb94e78 Apr 28, 2026
7 checks passed
@sergio-sisternes-epam sergio-sisternes-epam deleted the audit/complexity-review-worktree branch April 28, 2026 06:01
chaobo8484 added a commit to chaobo8484/apm that referenced this pull request Apr 28, 2026
Resolved conflicts in:
- CHANGELOG.md: combined PR microsoft#844/microsoft#848 changes with upstream microsoft#918 changes
- src/apm_cli/policy/parser.py: kept _looks_like_yaml_content pattern from upstream, added (OSError, ValueError) handling
- uv.lock: updated to version 0.10.0
danielmeppiel added a commit that referenced this pull request Apr 28, 2026
Resolves CHANGELOG.md conflict in the [Unreleased] section by combining
both sides:
- Added (HEAD): apps[]/app-id GitHub App auth entries for shared/apm.md
- Changed/Fixed (origin/main): #820 target validation, #918 cleanup,
  #1008 marketplace build GHE host

Both blocks are independent and additive.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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 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