Skip to content

refactor: tighten Stage 2 complexity thresholds (#1078)#1681

Open
sergio-sisternes-epam wants to merge 26 commits into
mainfrom
sergio-sisternes-epam/design-loop-issue-1078
Open

refactor: tighten Stage 2 complexity thresholds (#1078)#1681
sergio-sisternes-epam wants to merge 26 commits into
mainfrom
sergio-sisternes-epam/design-loop-issue-1078

Conversation

@sergio-sisternes-epam

@sergio-sisternes-epam sergio-sisternes-epam commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

TL;DR

Strangler Stage 2 (issue #1078): tighten the six Ruff complexity thresholds to their Stage 2 targets and fix every resulting violation across the APM CLI, delivered as one PR with per-subsystem logical commits for reviewability. File-length goes straight to 800 (no Stage 2B follow-up).

Draft / WIP. Landing subsystem-by-subsystem. The threshold flip in pyproject.toml / ci.yml is the last commit, so intermediate commits stay green at the current thresholds.

Final thresholds (enforced in the last commit)

Metric Stage 1 Stage 2
max-statements 200 120
max-args 15 12
max-branches 60 40
max-returns 12 8
max-complexity 50 35
file-length (MAX_LINES) 2100 800

Approach

For files that are both a complexity offender and a file-length offender, extract over-complex function bodies into new sibling submodules — this reduces complexity and file length in one move. Public command surfaces and @patch test seams are preserved via module-level re-exports. Prefer genuine simplification (deduplication, shared helpers, dispatch tables, parameter objects) over mechanical code-moving.

Progress

  • Commit 1a — commands/install.py split (2085 -> 765 lines) into six install/ submodules. Monkeypatch seams preserved; inline local-bundle duplication removed (R0801 green). 16645 unit + acceptance and 927 install integration tests pass.
  • Commit 1b — rest of install subsystem (resolve / pipeline / targets / services / sources)
  • Commits 2-7 — integration, deps, command layer + bundle, models + compilation, core + adapters, marketplace + policy
  • Commit 8 — flip pyproject.toml + ci.yml thresholds (enforcement, last)

Closes #1078.

Sergio Sisternes and others added 20 commits June 6, 2026 00:25
Split commands/install.py (2085 -> 765 lines) into six install/ submodules:
pkg_resolution, apm_packages, install_cmd_phases, mcp_handler,
manifest_rollback, cli_context. Reduces statements/branches/complexity and
file-length below the issue #1078 Stage 2 targets while preserving the public
command surface and @patch test seams via module-level re-exports.

- RULE A: moved helpers re-exported from commands.install so
  @patch('apm_cli.commands.install.*') targets keep resolving.
- RULE B: moved call sites route patched symbols through the commands.install
  module object (_m.) so monkeypatches intercept across module boundaries.
- Removed the duplicated inline local-bundle block; wired the extracted
  _try_local_bundle_install helper (R0801 duplication guardrail green).

Behaviour-preserving: 16645 unit + acceptance and 927 install integration
tests pass; ruff (std + final thresholds), ruff format, and R0801 all green.

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

Bring install/services.py under Stage 2 thresholds and <800 lines (859->684)
by reusing existing structure instead of moving code:

- integrate_local_content: collapse six separate integrator params into the
  existing IntegratorBundle (15->10 args).
- integrate_package_primitives: group optional knobs (skill_subset,
  scratch_root, policy) into a new frozen IntegrationOptions dataclass
  (14->12 args); extract per-kind/skill logging + cowork-warn helpers into
  new sibling install/services_integrate.py to clear C901/branch/stmt.
- integrate_local_bundle: extract slug validation helper to clear C901.

Monkeypatch seams preserved (both functions stay defined in services.py);
public names and backward-compat aliases re-exported. Behaviour unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… + LSPIntegrationContext (#1078)

Introduce MCPRequestSpec frozen dataclass (mcp/spec.py) grouping the six
shared MCP identity fields; run_mcp_install drops 15->10 args and
validate_mcp_conflicts 14->9 args by consuming it. Add LSPIntegrationContext
to lsp/integration.py so run_lsp_integration drops 15->8 args. Call sites in
mcp_handler.py, commands/install.py, apm_packages.py updated; unit/integration
call sites updated to match.

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

Move the ~285-line nested download_callback closure out of
_resolve_dependencies into a stateful _TransitiveDownloader callable in a
new sibling module install/phases/resolve_transitive.py. Ruff folds nested
closures into the parent function, so this clears the C901/PLR0915 hot spot
on _resolve_dependencies and drops resolve.py from 988 to 691 lines (under
the 800 file-length target); the new module is 344 lines.

The callable keeps parent_pkg in __call__ so APMDependencyResolver's
_signature_accepts_parent_pkg introspection still routes the declaring-parent
anchor for transitive local deps (#857). The registry branch stays inline in
__call__ so the dep_ref rebind remains visible to the shared except handler;
this is safe because get_unique_key() is invariant to registry_name. The
download accumulators (downloaded/failures/transitive_failures) are read back
onto ctx by identity, preserving post-resolution behaviour exactly.

Refs #1078

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Stage 2 file-length tightening for the install dependency-source
strategy. sources.py 886 -> 519 lines (<800) with no behavioural change.

- New sources_base.py: Materialization value object + DependencySource
  ABC, now with two hoisted helpers that fold the per-source lockfile
  bookkeeping that was copy-pasted into every acquire():
    * _lockfile_node_fields() -> (depth, resolved_by, is_dev)
    * _skip_integration(deltas) -> Materialization(package_info=None, ...)
- New sources_fresh.py: FreshDependencySource (network path) plus
  _format_package_type_label, moved verbatim.
- sources.py keeps Local + Cached + _rebuild_cached_semver_resolution +
  make_dependency_source, re-exports the moved symbols (with __all__) so
  every existing import path keeps working, and uses the new base helpers
  in Local/Cached.

Less code, same functionality: the three identical node-field blocks and
two identical skip-integration returns collapse into shared base helpers.
R0801 green.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Stage 2 complexity tightening for the install targets phase.
targets.py run() C901 45 -> ~8, PLR0915 143 -> ~35, PLR0912 50 -> ~7,
all well under the Stage 2 thresholds (35/120/40). File 576 -> 585.

Extracted five focused module-level helpers from run():
- _gate_cowork_target / _gate_copilot_app_target (kept separate: cowork
  has Linux-vs-other messaging + a project-scope gate copilot-app lacks)
- _resolve_project_scope_targets (v2 resolution block)
- _handle_user_scope_targets (user-scope logging + dir creation)
- _build_integrators (integrators dict)

run() is now a thin orchestrator. No behavioural change: call sites,
argument order, log messages, exit codes, and exception types preserved.

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

Move `_preflight_auth_check` into a new sibling `pipeline_preflight.py`
(re-exported for import compatibility) and extract three cohesive helpers
from `run_install_pipeline` -- `_read_early_lockfile_state`,
`_resolve_managed_files`, and `_run_skill_path_migration`.

This drops `run_install_pipeline` under the Stage 2 complexity thresholds
(statements/branches/mccabe) and pipeline.py under 800 lines, with no
behaviour change. `_run_phase` and `run_install_pipeline` remain
module-level (test monkeypatch surface preserved).

Refs #1078

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

Extract the pure stateless hook transforms into `hook_transforms.py` and the
source-marker / dependency-scan / merge helpers into `hook_merge.py`, and
decompose `_integrate_merged_hooks` into a thin orchestrator over focused
helpers. A shared `_copy_hook_scripts` removes a duplicated script-copy loop.

hook_integrator.py drops 1738 -> 769 lines and `_integrate_merged_hooks`
falls under the Stage 2 complexity thresholds (mccabe/branches/statements),
with no behaviour change. Module-level names tests patch/import
(`HookIntegrator`, `_rich_warning` via wrapper, the re-exported transforms)
are preserved.

Refs #1078

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

Decompose skill_integrator.py (1827 lines) into cohesive siblings:
skill_naming, skill_orchestrate, skill_plugin, skill_sync, skill_deploy.
skill_integrator now layers over skill_deploy; get_effective_type and
should_install_skill remain module-level to preserve monkeypatch targets.
validate_skill_name return count reduced under the Stage 2 max-returns gate.

Part of #1078 (Strangler Stage 2). Behaviour-preserving; full unit,
acceptance and integration suites green.

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

Move the TOCTOU-safe _read_bytes_no_follow + _SymlinkRaceError into a
cohesive base_integrator_io sibling (re-exported, so the original
import/patch path is preserved). Collapse validate_deploy_path's tail to a
single return (PLR0911 9->8) and reuse the existing partition_bucket_key
helper in partition_managed_files instead of inlining the alias lookup twice.

base_integrator.py 812->769 lines (under the 800 gate). Part of #1078
(Strangler Stage 2). Behaviour-preserving; full suites green.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move RULE_FORMATS, PrimitiveMapping and TargetProfile out of targets.py into
a focused target_profile module; targets.py re-exports all three so the
178x-imported public surface (and KNOWN_TARGETS, which stays put) is
unchanged. No behaviour change; the dataclasses are self-contained.

targets.py 1036->631 lines (under the 800 gate). Part of #1078 (Strangler
Stage 2). Full unit, acceptance and integration suites green.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Decompose _resolve_target_runtimes (C901 50, PLR0912 54, PLR0915 132)
into in-module patch-safe helpers: _detect_installed_runtimes (+ fallback
and a data-driven _runtime_opted_in predicate that collapses the parallel
cursor/opencode/gemini/windsurf opt-in branches via _DIR_GATED_RUNTIMES),
_intersect_script_runtimes, and _apply_user_scope_filter. find_runtime_binary
stays a module global so the 38 monkeypatch sites remain valid.

Fix _install_registry_group PLR0913 (13 args) with a cohesive
_RegistryDepGroup parameter object (deps/names/dep_map), keeping the
callable behaviour stable.

All six Stage-2 thresholds now clean for this file; 785 lines (<800).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Extract the JSON/TOML/Claude config-cleanup trio into a new
mcp_config_clean.py, and collect_transitive / _install_for_runtime /
_gate_project_scoped_runtimes bodies into a new mcp_runtime_ops.py.
MCPIntegrator keeps thin delegating staticmethods so the heavily
monkeypatched MCPIntegrator.<name> call/patch surface is unchanged;
patched module globals (_rich_success, Path) are routed back through
mcp_integrator from the siblings so the patch targets stay honored.

mcp_integrator.py 1150 -> 765 lines (file-length offender cleared).
File-length backlog 30 -> 29.

Issue: #1078

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

Strangler Stage 2 (#1078), Commit 3a: tighten the deps subsystem's
lower-risk files toward the 800-line guardrail and the final complexity
thresholds, preserving the test monkeypatch surface.

File-length offenders cleared (siblings extracted, delegating wrappers
keep patch targets intact):
- apm_resolver.py 1028 -> 777 (new apm_resolver_helpers.py)
- plugin_parser.py 916 -> 632 (new plugin_server_helpers.py)
- bare_cache.py 805 -> 724 (new bare_cache_msg.py)

Complexity-only (PLR0911) fixes, behaviour-preserving:
- git_reference_resolver.py: extract _call_commits_api; merge guard
- lockfile.py: combine two equality guards into one return
- registry/outdated.py: merge early-exit guards, preserve source strings

bare_cache failure-message builder uses a genuine parameter object:
build_clone_failure_message now takes a required CloneFailureContext
(bundling the six failure-classifier flags) instead of flat kwargs;
clone_engine.py (sole caller) constructs it at the call site. No
**kwargs threshold-gaming; 13 call args -> 8 params.

backlog 29 -> 26; R0801 10.00/10; 16645 unit+acceptance pass;
2776 targeted integration pass.

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

Strangler Stage 2 (#1078), Commit 3b: bring the two largest deps files
under the 800-line guardrail and the final complexity thresholds while
preserving the heavy test monkeypatch surface.

File-length offenders cleared via delegating wrappers + ops siblings
(every new module is <=525 lines, well under budget):
- github_downloader.py 1806 -> 737; bodies moved to
  github_downloader_setup_ops.py (247), github_downloader_package_ops.py
  (525), github_downloader_subdir_ops.py (448)
- download_strategies.py 1123 -> 587; bodies moved to
  download_strategies_ops.py (379), download_strategies_backends_ops.py
  (416); DownloadDelegate class stays in download_strategies.py

Complexity offenders decomposed in their new homes (genuine cohesive
helpers, distinct paths kept separate -- NOT collapsed):
- download_subdirectory_package C901/PLR0912/PLR0915 -> per-cache-tier
  helpers (persistent / shared-bare / legacy-clone) + extract/build/log
- download_github_file C901/PLR0912 -> per-step + per-backend helpers

Patch-safety: thin class wrappers keep patched method names; moved
bodies route patched module globals via function-level _gh./_ds.
aliases (Rule B); inter-ops calls route through the class wrappers so
no ops module imports another at module scope (no cycles). No **kwargs
gaming, no noqa, no threshold edits.

backlog 26 -> 24; R0801 10.00/10; 16645 unit+acceptance pass;
3030 targeted integration pass.

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

Strangler Stage 2, Commit 4 of 8. Drive the commands and bundle
subsystems under the 800-line file guardrail and clear their
second-tier complexity offenders, preserving behaviour and the
monkeypatch/import surface via re-export shims and delegating wrappers.

Length splits (all resulting modules < 800):
- marketplace/__init__.py 1601 -> 535 (+_table_ops 350, _registry_cmds
  543, _publish_ops 261, _search_cmd 119)
- commands/audit.py 1161 -> 758 (+_audit_ops 372)
- commands/deps/cli.py 925 -> 673 (+_cli_ops 273)
- commands/pack.py 808 -> 694 (+_pack_ops 168)
- compile/cli.py 1002 -> 671 (+_run_ops 417)

Complexity-only fixes (genuine simplification, no suppression):
- compile _run_compilation: 13 args -> 5 via frozen CompilationRunConfig
  parameter object constructed at the call site
- marketplace doctor run_doctor: extracted 8 cohesive check helpers
- uninstall, outdated, find, plugin_exporter, local_bundle: merged
  early-return guards and extracted cohesive helpers to clear
  PLR0911/PLR0912/PLR0915/C901

File-length backlog 24 -> 19. Complexity gate clean at final Stage-2
thresholds; thresholds themselves flip in the final enforcement commit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Strangler Stage 2, Commit 5 of 8. Drive the models/dependency and
compilation subsystems under the 800-line file guardrail and clear
their second-tier complexity offenders, preserving behaviour and the
monkeypatch/import surface via reusable mixin classes.

models/dependency/reference.py 1985 -> 702 (largest file in the repo):
- DependencyReference now composes three mixins -- _ReferenceParseMixin
  (_reference_parse.py), _ReferenceUrlMixin (_reference_url.py),
  _ReferenceShorthandMixin (_reference_shorthand.py). The dataclass and
  composed symbol stay defined in reference.py so the patch surface is
  unchanged.
- Leaf helpers/constants moved to _reference_util.py and re-exported
  from reference.py; this breaks the import cycle (mixins import the
  util, never the parent at module scope; back-references are
  TYPE_CHECKING-only).
- parse_from_dict (C901) decomposed into shape-routed sub-parsers with
  two shared validators that genuinely de-duplicate the repeated
  alias/ref grammar.
- models/validation.py: validate_apm_package PLR0911 9 -> 4 returns via
  a type-dispatch helper.

compilation:
- agents_compiler.py 1498 -> 785 (+_agents_emit, _agents_output mixins)
- context_optimizer.py 1328 -> 548 (+_placement_solver, _pattern_matcher)
- distributed_compiler.py 804 -> 658 (+_distributed_orphans)
- link_resolver.py PLR0911 10 -> 8 returns via merged guards
- moved methods that reference patched module globals (resolve_markdown_links,
  _logger, Path) route back through the origin module at call time so
  existing monkeypatches still apply.

File-length backlog 19 -> 15. Complexity gate clean at final Stage-2
thresholds; thresholds flip in the final enforcement commit.

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

Strangler Stage 2 second-tier complexity tightening, subsystem 6 of 7.
Clears the 8 remaining >800-line offenders in core/, adapters/, cache/,
utils/ and output/ via mixin/leaf-module splits, plus in-place complexity
fixes - net -144 lines (genuine reduction despite the mandated splits).

Length splits (all parents + new siblings <800):
- core/auth.py 1142->670 (+_auth_support.py 532 mixin). Auth boundary
  preserved: get_bearer_provider/ls-remote methods stay in auth.py; the
  sibling reaches the ADO bearer provider via a new
  AuthResolver._ado_bearer_provider() accessor so Rule A stays clean.
  try_with_fallback de-duplicated (PLR0911 12->8, was failing on main).
- core/script_runner.py 1135->686 (+_prompt_compiler.py 183,
  +_runtime_commands.py 302 mixin).
- core/command_logger.py 836->221 (+_install_logger.py 599). InstallLogger
  re-exported lazily via PEP 562 __getattr__ to break the subclass<->base
  import cycle while preserving the command_logger._rich_*/CommandLogger
  patch surface.
- cache/git_cache.py 830->599 (+_git_cache_bare.py 279 mixin).
- utils/github_host.py 806->659 (+_github_host_artifactory.py 173).
- adapters/client/base.py 866->434 (+_base_env.py 483 mixin).
- adapters/client/copilot.py 1056->697 (+_copilot_env.py 388 mixin);
  CopilotClientAdapter override MRO preserved (_CopilotEnvMixin first).
- output/formatters.py 994->484 (+_formatters_detail.py 465 mixin);
  removed _format_final_summary duplicate (byte-identical to
  _format_results_summary) + its duplicate test classes in two test files.

Complexity-only (in place, no split): target_detection.py C901 via dict
dispatch (+list-arg guards); primitives/parser.py PLR0911 12->3;
cache/integrity.py PLR0911 9->8.

Rule B routing added for every moved reference to a patched module global.
No linter-gaming (no **kwargs, no complexity noqa; one obsolete noqa removed).

Verification: ruff + format clean; complexity gate clean at final Stage-2
thresholds; pylint R0801 10.00/10; backlog 15->7; full unit+acceptance
16605 passed; targeted integration 3119 passed; auth-signals lint clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…r 800 (#1078)

Strangler Stage 2, Commit 7: drive the remaining seven >800-line files in
the policy/ and marketplace/ subsystems under the 800-line guardrail via
leaf-module and mixin splits that preserve every patched/public import
surface, plus genuine PLR0911 decompositions.

policy/
- discovery.py 1364->662: extract cache cluster to _discovery_cache.py
  and chain/host-pin helpers to _discovery_chain.py (Rule B routes the
  patched requests/subprocess/discover_policy/_write_cache globals).
  _fetch_github_contents PLR0911 11->2 via _parse_github_repo_ref /
  _decode_github_content / _call_github_api.
- policy_checks.py 1134->693: extract MCP/compilation/manifest checks to
  _policy_checks_mcp.py; run_policy_checks PLR0911 9->5. _check_unmanaged_files
  + _MAX_UNMANAGED_SCAN_FILES kept in place to preserve a monkeypatch.
- ci_checks.py run_baseline_checks PLR0911 11->6 (lazy lambda loop).
- _constraint_pinning.py classify_unbounded_reason PLR0911 10->8.

marketplace/
- yml_schema.py 1220->462: dataclasses to _yml_models.py (leaf), parsers
  to _yml_parsers.py (imports models, no cycle); dedup _parse_outputs.
- builder.py 1130->506: report dataclasses to _builder_reports.py, resolve
  methods to _BuilderResolveMixin in _builder_resolve.py (Rule B for the
  20x-patched urllib); dedup the _compute_diff plugin-SHA loop.
- resolver.py 953->696: cross-repo-misconfig/matching cluster to
  _resolver_match.py; two PLR0911 9->5 via guard extraction. Public
  parse_marketplace_ref/get_marketplace_by_name/resolve_marketplace_plugin stay.
- client.py 817->717: cache I/O cluster to _client_cache.py. Public
  fetch_marketplace/fetch_or_cache/clear_marketplace_cache/search_marketplace stay.
- publisher.py 922->759: PublishState + dataclasses to _publish_state.py;
  _process_single_target PLR0911 9->8.
- semver.py PLR0911 11->7 via _satisfies_caret; public API unchanged.

All resulting files (parents + 10 new siblings) <800 (<=770). Whole-src
>800 backlog 7->0. Full unit+acceptance 16605 passed; targeted integration
2747 passed; ruff/format/complexity(final thresholds)/R0801 10.00/auth-signals
all clean. No threshold flip yet (enforcement is the final commit).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Final Strangler Stage 2 commit: flip the guardrails now that all
violations are fixed (commits for integration, deps, commands, models,
core/adapters, policy, marketplace landed earlier in this PR).

pyproject.toml [tool.ruff.lint]: tighten to Stage 2 targets
  max-statements 200->120, max-args 15->12, max-branches 60->40,
  max-returns 12->8, mccabe max-complexity 50->35; refresh the stale
  Stage-1 roadmap comments.
.github/workflows/ci.yml: file-length guardrail MAX_LINES 2100->800;
  refresh the stale comment.
CHANGELOG.md: record the threshold + guardrail tightening under Unreleased.

tests/integration/test_deps_resolver_{phase3b,resolution}.py: the new
max-args=12 surfaced a 15-arg `_make_dep_ref` test factory in both files.
Dropped the never-passed `explicit_scheme` param and grouped the three
cohesive Azure DevOps coordinates into a single `ado` triple (15->12 args)
- a parameter-object simplification, not an arg-count dodge.

Verification (CI-mirror, all green): ruff check src/ tests/; ruff format
--check; file-length guardrail (no src file >800); R0801 10.00/10;
auth-signals; YAML and relative_to guards; full unit+acceptance 16605
passed; targeted integration green. Whole-src >800 backlog is 0.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sergio-sisternes-epam sergio-sisternes-epam marked this pull request as ready for review June 6, 2026 06:47
Copilot AI review requested due to automatic review settings June 6, 2026 06:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

@sergio-sisternes-epam sergio-sisternes-epam added the panel-review Trigger the apm-review-panel gh-aw workflow label Jun 6, 2026
Sergio Sisternes and others added 3 commits June 9, 2026 09:56
Resolve conflicts between the Stage 2 refactor (extract-to-sibling-module)
and main's behavioural changes by accepting the refactored structure at each
conflict site and porting main's semantic deltas into the relocated code:

- #1671 ADO HTML sign-in detection + AAD bearer fallback ->
  _ado_build_headers / _ado_check_html_signin in download_strategies_backends_ops.py
- #1666 preserve existing apm.yml during plugin synthesis ->
  _generate_apm_yml(existing_manifest=...) + _read_lsp_json envelope-unwrap
- #1678 compile target vscode/agents family collapse -> _resolve_list_target
- openclaw-target feature -> install/phases/targets.py taken wholesale
- _REF_VERSION_SUFFIX_RE re-exported from _reference_util.py

Auto-merged both-side files verified to retain main's deltas in the live path
(scoped MCP names, GHES GITHUB_HOST detection, target-aware AGENTS.md dedup).

Reduce three files pushed over the 800-line CI limit by main's additions via
behaviour-preserving extraction to sibling modules:
- commands/audit.py 816 -> 737 (_resolve_external_options, _run_external_scanners
  -> _audit_ops.py)
- core/target_detection.py 804 -> 787 (_target_error, format_provenance
  -> _target_detection_helpers.py)
- compilation/agents_compiler.py 802 -> 782 (_detect_deployed_instructions
  -> _compiler_helpers.py)

Lint chain (ruff check/format, pylint R0801 10.00/10, auth-signals, file-length,
YAML I/O, relative_to) green; 779 targeted tests across touched subsystems pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Brings in publish zip archive format (#1695) and maintainers update (#1704).
No conflicts: neither commit touches files changed by this branch.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolve conflict in skill_integrator.py from #1709 (honor --skill subset
on non-package skill-collection install). My refactor extracted skill
promotion into skill_deploy.py; main's #1709 edited the inline original.

Resolution: keep HEAD's thin-delegator structure; port #1709 semantics
into the relocated impl:
- Add _skill_subset_name_filter() normalization helper to skill_deploy.py
  (matches --skill by raw name, normalized path, or leaf name).
- Thread skill_subset -> name_filter through _promote_sub_skills_standalone
  (the #1707 fix: standalone/non-package-skill path now honors --skill).
- skill_orchestrate.py: pass skill_subset on both standalone promotion
  routes (MARKETPLACE_PLUGIN / no-root-SKILL.md packages).
- _integrate_skill_bundle uses the normalized helper.

Shadow gate green: ruff, ruff format, pylint R0801 10.00/10, auth-signals,
import smoke, 378 targeted tests pass. Files within 800-line guard
(skill_integrator.py 458, skill_deploy.py 789).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sergio Sisternes and others added 3 commits June 9, 2026 15:41
The #1709 port left two copies of the subset-normalization helper: a
module function in skill_deploy.py and an identical SkillIntegrator
staticmethod. CI's pylint R0801 guard (evaluated on the merge commit)
flagged the 15-line duplication. Make the staticmethod delegate to the
skill_deploy module function, matching the file's delegator pattern.

R0801 now exits 0; ruff/format clean; 378 targeted tests pass.

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

Sync main commits #1700, #1676, #1694, #1710. The #1700 feature (surface
installed hook actions during install) wove display-payload tracking
through code my refactor extracted into sibling helpers; resolve two
conflicts by keeping the extracted structure and porting #1700 semantics:

- services.py: accept the extracted _log_per_kind_results() call; move
  _log_hook_display_payloads into services_integrate.py (avoids circular
  import) and re-export it from services.py; emit hook summaries inside
  _log_per_kind_results.
- hook_integrator._integrate_merged_hooks: accept extracted
  _merge_hook_file_entries / _write_merged_config; thread per-file
  display data out via a new optional capture_entries kwarg on
  _merge_hook_file_entries; build display payloads after
  _write_merged_config finalizes (post _apm_source strip).
- Keep hook_integrator.py <=800 by relocating _iter_hook_entries /
  _summarize_command / _build_display_payload to hook_transforms.py and
  _parse_hook_json to hook_merge.py as thin delegators.

Shadow gate green: ruff, ruff format, pylint R0801 10.00/10 (EXIT=0),
auth-signals, import smoke, 3178 tests pass. All files within 800-line
guard (hook_integrator.py 799, hook_merge.py 766, hook_transforms.py 601,
services.py 690, services_integrate.py 307).

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

Sync with main through v0.19.0 (#1715, #1714, #1718, #1719, #1717).

Conflicts resolved:
- CHANGELOG.md: main cut the v0.19.0 release, moving prior Unreleased
  entries into a dated section. Took main's restructure; relocated the
  #1681 "Tightened Stage 2 code-complexity thresholds" entry to the new
  ## [Unreleased] section (released sections stay immutable per Keep a
  Changelog). Took main's #1702/#1710 close-ref for the lockfile entry.
- src/apm_cli/install/services.py: #1718 added per-file skill-bundle
  integrity recording inline at a site my refactor had extracted into
  _log_skill_result. Kept the new _skill_bundle_file_entries helper in
  services.py (sibling of _deployed_path_entry); dropped main's duplicate
  inline _log_hook_display_payloads (already relocated to
  services_integrate.py and re-exported in my #1700 port); accepted the
  _log_skill_result delegator at the call site.
- src/apm_cli/install/services_integrate.py: ported #1718's
  deployed.extend(_skill_bundle_file_entries(tp, ...)) into
  _log_skill_result's deployed-path loop (deployed aliases
  result["deployed_files"]), with a lazy import of the helper.

Shadow gate: no markers, all src <=800 lines, ruff check + format clean,
pylint R0801 EXIT=0, auth-signals clean; 11144 tests pass across the
install/integration suites plus the #1718, #1700, and #1709 targeted sets.

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.

Strangler Stage 2: second-tier complexity tightening

2 participants