Skip to content

fix(install): fail loudly when a package deploys to no target (closes #2796) - #2806

Merged
Daniel Meppiel (danielmeppiel) merged 5 commits into
mainfrom
bbs/fix-2796-plugin-target-noop
Sep 9, 2026
Merged

Daniel Meppiel (danielmeppiel) merged 5 commits into
mainfrom
bbs/fix-2796-plugin-target-noop

Conversation

@danielmeppiel

@danielmeppiel Daniel Meppiel (danielmeppiel) commented Sep 4, 2026 •

Copy link
Copy Markdown
Collaborator

Context: Agent Plugins v1.0.0 packages can only register natively with the copilot target today. When every selected target was non-copilot, apm install --skill ... --target codex skipped the package, deployed nothing, printed No changes, and exited 0.

WHY: A successful no-op hid an unsatisfied install request. The install outcome now fails only when target exclusion leaves the run with zero deployed packages, while mixed installs that deploy at least one package still exit 0.

BREAKING CHANGE: apm install now exits non-zero when Agent Plugin target exclusion means no package was deployed. Migration: install a direct skill subpath such as apm install kunchenguid/lavish-axi/skills/lavish#main --target codex, or select --target copilot for native Agent Plugin registration.

Scenario Evidence:

Scenario Principle Test path Run evidence
Agent Plugin selected only for non-Copilot targets fails loudly, preserves the requested ref in the skill-subpath hint, and commits no durable state. DevX; multi-harness support tests/integration/test_required_lifecycle_state_machine.py::test_required_agent_plugin_target_exclusion_noop_fails_without_mutating_state APM_E2E_TESTS=1 uv run --extra dev pytest tests/integration/test_required_lifecycle_state_machine.py::test_required_agent_plugin_target_exclusion_noop_fails_without_mutating_state -q -> 1 passed in 2.50s
Mixed installs stay successful when a target-excluded Agent Plugin deploys nothing but another package deploys. DevX; multi-harness support tests/unit/install/test_agent_plugin_deployment_boundary.py::test_target_exclusion_does_not_hard_fail_when_the_rest_of_the_batch_installs uv run --extra dev pytest tests/unit/install/test_agent_plugin_deployment_boundary.py::test_target_exclusion_hint_names_remote_skill_subpath_form tests/unit/install/test_agent_plugin_deployment_boundary.py::test_target_exclusion_fails_when_no_package_is_deployed tests/unit/install/test_agent_plugin_deployment_boundary.py::test_target_exclusion_does_not_hard_fail_when_the_rest_of_the_batch_installs -q -> 10 passed in 2.44s

How to test:

  • uv run --extra dev pytest tests/unit/install/test_agent_plugin_deployment_boundary.py::test_target_exclusion_hint_names_remote_skill_subpath_form tests/unit/install/test_agent_plugin_deployment_boundary.py::test_target_exclusion_fails_when_no_package_is_deployed tests/unit/install/test_agent_plugin_deployment_boundary.py::test_target_exclusion_does_not_hard_fail_when_the_rest_of_the_batch_installs -q
  • uv run --extra dev pytest tests/unit/copilot_plugins/test_install_lifecycle.py -q
  • APM_E2E_TESTS=1 uv run --extra dev pytest tests/integration/test_required_lifecycle_state_machine.py::test_required_agent_plugin_target_exclusion_noop_fails_without_mutating_state -q
  • uv run --extra dev ruff check src/ tests/
  • uv run --extra dev ruff format --check src/ tests/

Architecture: owner-extension. The deterministic owner-touch gate reports src/apm_cli/install/outcome.py under the canonical decision Install success / failure outcome; this PR extends that existing owner with the target-excluded no-deploy failure case while keeping per-package target exclusion detection in the Agent Plugin deployment boundary and diagnostics collector.

apm-spec-waiver: APM CLI-specific Agent Plugin target-exclusion UX; OpenAPM manifest and registry semantics are unchanged.

closes #2796

Copilot AI lite review requested due to automatic review settings September 4, 2026 19:05
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

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 review overview

🟢 Approval recommended

The changes correctly implement the specified failure condition with targeted diagnostics and comprehensive unit coverage for the new behavior (pure no-op, mixed batch, and dry-run).

Review tier: Lite
Findings: None

What changed in this PR

This PR updates the apm install outcome logic so that an Agent Plugins v1.0.0 package that is excluded by target selection (i.e., no selected target can receive it) is no longer treated as a successful no-op. Instead, the install fails (non-zero exit) only when that exclusion results in zero packages being deployed, matching the intent described in issue #2796.

Changes:

  • Add a dedicated diagnostic subtype/count for “Agent Plugin target excluded” warnings and plumb it into install outcome classification.
  • Update the install outcome owner to fail when installed_count == 0 and at least one Agent Plugin was target-excluded.
  • Expand/adjust unit tests to cover: pure-no-op failure, mixed-batch non-fatal behavior, dry-run preview remaining non-fatal, and the actionable “install via /skills/” hint.
File Description
tests/​unit/​install/​test_agent_plugin_deployment_boundary.py Updates and adds regression tests for the new “fail loudly on pure no-op” behavior, including hint wording and dry-run parity.
src/​apm_cli/​utils/​diagnostics.py Extends DiagnosticCollector.warn() with a severity subtype and adds a first-class Agent Plugin target-exclusion warning + counter.
src/​apm_cli/​install/​template.py Builds an actionable skip message (including /skills/<skill> workaround) and records target exclusion using the new diagnostic subtype.
src/​apm_cli/​install/​outcome.py Adds agent_plugin_target_excluded_count() helper and fails the install when exclusion caused a zero-deploy run.
CHANGELOG.md Documents the breaking change and migration guidance under [Unreleased].

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Addresses panel follow-ups to keep Agent Plugin target-exclusion recovery commands pinned to the selected ref, document the breaking no-op contract, and add real CLI lifecycle coverage for total no-op failure plus mixed-install success.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Addresses the CI regression from the legacy exit-0 expectation by rewriting the Agent Plugin non-Copilot target test for issue #2796: a total target-exclusion no-op now fails and commits no durable state.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Incorporate current main and bind req-tg-011 to the renamed no-op lifecycle regression without weakening the exclusion contract. Preserves mixed-batch success and direct-skill recovery for #2796 / #2806.

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

Daniel Meppiel (danielmeppiel) commented Sep 9, 2026 •

Copy link
Copy Markdown
Collaborator Author

APM Review Panel: ship_now

APM #2806 makes total Agent Plugin target exclusion fail honestly, preserves successful mixed installs, and supplies inventory-grounded, ref-preserving direct-skill recovery.

cc Sergio Sisternes (@sergio-sisternes-epam) -- a fresh advisory pass is ready for your review.

Reviewed HEAD 9fc5127 against origin/main f8df1b7. All eight active reviewers report no findings; auth remains inactive after complete fallback scope inspection. The canonical install-outcome owner handles zero-deployment target exclusion without introducing a competing decision path. The reservation is preserved: successful mixed batches remain successful, direct-skill recovery stays actionable, and non-dry-run total target exclusion must not report success.

All four previous follow-ups are closed in source. The mixed lifecycle scenario now requires ordinary SKILL.md files and compares deployed bytes with fixture bytes. Recovery matches requested skill names or directories against canonical inventory, uses directory_name, and explicitly identifies an available alternative when necessary. shlex.quote protects the complete dependency path and retained ref, with POSIX-shell labeling. Documentation qualifies the failure as non-dry-run and links the canonical dry-run exception. The breaking exit-code change and migration are documented, supporting P4 and P6 without implying additional native target support.

Evidence remains bounded: the parent reports 64 targeted passes in 12.99s at the identical precommit tree, with exact-HEAD execution still parent-owned. Recorded mutation failures are intentional detections with guards restored, not current-code failures. req-tg-011 now binds to the renamed test; this is a source repair, not a conformance pass or waiver, and historical PR-body waiver text is not current evidence. Neither reviewers nor this synthesis executed validation. Fresh functional, conformance, lint and CI evidence remains parent-owned; current CI green is not established.

Aligned with: Pragmatic as npm: P4/P6: truthful install outcomes, useful mixed-batch success, and explicit preview limits preserve predictable package-manager behavior. Multi-harness / multi-host: P2/P3/P5: inventory-grounded direct-skill recovery retains the selected compatible target and requested ref without forcing native Copilot registration. Secure by default: Complete-argument POSIX quoting protects copied recovery commands from shell reinterpretation of dependency paths and refs.

Panel summary

Persona B R N Takeaway
Python Architect 0 0 0 Prior architecture finding resolved. Canonical inventory drives recovery; the outcome owner preserves mixed success and rejects zero-deployment exclusion.
CLI Logging Expert 0 0 0 No logging concerns: total exclusion reports failure; mixed success and inventory-grounded, ref-preserving recovery remain intact.
DevX UX Expert 0 0 0 Prior recovery finding resolved: inventory-backed hints preserve refs; total exclusion fails, mixed success remains, and dry-run behavior is documented.
Supply Chain Security 0 0 0 Complete-argument quoting resolves prior feedback; total exclusion fails before cleanup, while mixed success and scoped rollback remain intact.
OSS Growth Hacker 0 0 0 Recovery is inventory-grounded and copyable; documentation explains failed total exclusions, successful mixed installs, and dry-run limits. No growth concerns remain.
Doc Writer 0 0 0 Prior documentation follow-up resolved: non-dry-run failure, successful mixed batches, direct-skill recovery, and preview limits are documented.
Test Coverage 0 0 0 Previous artifact gap is resolved; no-op, mixed-success, inventory and quoting regressions have targeted assertions. Exact-head execution remains parent-owned.
Performance Expert 0 0 0 No scoped performance regression found: inventory lookup uses set membership, and exclusion counting remains linear at outcome boundaries.

B = blocking-severity findings, R = recommended, N = nits.
Counts are signal strength, not gates. The maintainer ships.

Architecture

classDiagram
    direction LR
    class DependencySource {
      <<Strategy>>
      +acquire() Materialization
    }
    class LocalDependencySource
    class CachedDependencySource
    class FreshDependencySource
    DependencySource <|-- LocalDependencySource
    DependencySource <|-- CachedDependencySource
    DependencySource <|-- FreshDependencySource
    class Materialization {
      <<Dataclass>>
      +package_info
      +deltas
    }
    class InstallContext {
      +installed_count
      +skill_subset
      +diagnostics
    }
    class InstallTemplate {
      <<Module>>
      +run_integration_template()
      +_record_agent_plugin_target_skip()
      +_agent_plugin_skill_name_for_hint()
    }
    note for InstallTemplate "Template Method: shared post-acquisition integration in install/template.py"
    class AgentPlugin {
      <<ImmutableIR>>
      +components
    }
    class AgentPluginComponents {
      +skills
    }
    class AgentPluginSkill {
      +directory_name
      +name
    }
    class DiagnosticCollector {
      +agent_plugin_target_excluded()
      +agent_plugin_target_excluded_count
      +render_summary()
    }
    class Diagnostic {
      <<Dataclass>>
      +category
      +severity
      +message
    }
    note for DiagnosticCollector "Collect-then-render: typed exclusion fact, aggregate classification, then presentation"
    class InstallOutcome {
      <<Module>>
      +result_from_install_context()
      +finalize_install_result()
      +apply_install_command_outcome()
    }
    class InstallResult {
      <<Dataclass>>
      +installed_count
      +disposition
      +exit_code
    }
    class InstallTransaction {
      +complete()
      +commit()
      +rollback()
    }
    DependencySource --> InstallContext
    DependencySource ..> Materialization : returns
    Materialization ..> AgentPlugin : package_info.package.agent_plugin
    AgentPlugin *-- AgentPluginComponents
    AgentPluginComponents *-- AgentPluginSkill
    InstallTemplate ..> Materialization
    InstallTemplate ..> AgentPluginSkill : reads canonical inventory
    InstallTemplate ..> DiagnosticCollector : records exclusion
    InstallContext --> DiagnosticCollector
    DiagnosticCollector *-- Diagnostic
    InstallOutcome ..> InstallContext
    InstallOutcome ..> DiagnosticCollector : reads facts
    InstallOutcome ..> InstallResult : owns classification
    InstallTransaction ..> InstallResult : consumes disposition
    class InstallTemplate:::touched
    class InstallOutcome:::touched
    class DiagnosticCollector:::touched
    class Diagnostic:::touched
    classDef touched fill:#fff3b0,stroke:#d47600
Loading
flowchart TD
    A["apm install: install()<br/>src/apm_cli/commands/install.py"] --> B["run_install_pipeline()<br/>src/apm_cli/install/pipeline.py"]
    B --> D{"dry-run?"}
    D -->|yes| P["[I/O] preflight_agent_plugin_dry_run()<br/>install/template.py<br/>Target exclusion is swallowed; structural errors still propagate"]
    P --> PV["DRY_RUN preview: exit 0 for target exclusion<br/>No recovery diagnostic or certified deployment"]
    D -->|no| AC["[NET] [FS] download.run() and source.acquire()<br/>install/phases/download.py; install/sources.py"]
    AC --> PF["preflight_agent_plugin_materializations()<br/>install/template.py<br/>Target exclusion does not abort the batch"]
    PF --> T["run_integration_template() -> _integrate_materialization()<br/>install/template.py: each dependency"]
    T --> E{"enforce_agent_plugin_deployment_boundary()<br/>agent_plugins/errors.py"}
    E -->|target excluded| S["_record_agent_plugin_target_skip()<br/>installed delta = 0; deployed_files = []"]
    S --> H["_agent_plugin_skill_name_for_hint()<br/>install/template.py<br/>Read AgentPlugin.components.skills"]
    H --> HS{"inventoried skill available?"}
    HS -->|yes| HC["_agent_plugin_target_skip_message()<br/>Match requested name or directory; otherwise available alternative<br/>Quote complete skill path plus retained ref"]
    HS -->|no| HF["_agent_plugin_target_skip_message()<br/>Copilot or target-compatible-package fallback"]
    HC --> W["DiagnosticCollector.agent_plugin_target_excluded()<br/>utils/diagnostics.py"]
    HF --> W
    E -->|structural error| ER["_record_agent_plugin_boundary_failure()<br/>Record error; installed delta = 0"]
    E -->|admitted or ordinary| I["[FS] integrate_package_primitives()<br/>install/services.py<br/>Record deployment deltas"]
    W --> AG["integrate.run()<br/>install/phases/integrate.py<br/>Aggregate installed_count across batch"]
    ER --> AG
    I --> AG
    AG --> O["result_from_install_context() -> finalize_install_result()<br/>install/outcome.py"]
    O --> Q{"Existing failure condition OR<br/>installed_count == 0 and exclusion_count > 0?"}
    Q -->|yes| F["FAILED: return before cleanup/finalize"]
    Q -->|no| C["[FS] [LOCK] Remaining pipeline phases<br/>install/phases/finalize.py: finalize.run()"]
    F --> RB["[FS] [LOCK] InstallTransaction.complete()<br/>install/transaction.py: rollback"]
    C --> CM["[FS] [LOCK] InstallTransaction.complete()<br/>Commit successful result"]
    RB --> R["[I/O] render_post_install_summary()<br/>install/summary.py<br/>Consume canonical disposition"]
    CM --> R
    R --> X{"apply_install_command_outcome()<br/>install/outcome.py"}
    X -->|FAILED| X1["exit 1; success summary disallowed"]
    X -->|SUCCESS or PARTIAL_SUCCESS| X0["exit 0; successful mixed batch preserved"]
Loading

Recommendation

Recommend shipping the reviewed code with no remaining code follow-ups. This advisory stance is not policy approval or proof of current CI success. The parent should establish exact-head validation evidence for the maintainer's shipping decision; unexpected failures warrant reassessment, not speculative findings now.


Full per-persona findings

Python Architect

No findings.

CLI Logging Expert

No findings.

DevX UX Expert

No findings.

Supply Chain Security

No findings.

OSS Growth Hacker

No findings.

Auth Expert -- inactive

Complete exact-head diff fallback check confirms src/apm_cli/install/outcome.py, src/apm_cli/install/template.py, src/apm_cli/utils/diagnostics.py and accompanying tests/docs change outcomes and recovery hints, not credentials, host classification or authentication semantics.

Doc Writer

No findings.

Test Coverage

No findings.

Performance Expert

No findings.

This panel is advisory. It does not block merge. Re-apply the
panel-review label after addressing feedback to re-run.

Fold #2806 advisory follow-ups: select an inventoried skill directory, quote POSIX command operands, assert mixed-install skill bytes, and document dry-run behavior. Drop release-note duplicates resurrected while incorporating main. Mutation probes reject missing outcome, inventory and quoting guards.

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

Copy link
Copy Markdown
Collaborator Author

Shepherd completion: code complete, required review outstanding

Head 9fc512786be6e0ca6fb079eb13964339af7a8f22 incorporates current main
f8df1b751efc30b32dc01b125616b51f777b4c81. The final advisory panel recommends
ship_now, with no remaining code follow-ups. All current CI checks pass.
GitHub reports MERGEABLE / BLOCKED because required human review remains.
Sergio Sisternes (@sergio-sisternes-epam): please review the latest push as the nonauthor CODEOWNER.
No merge or merge-queue entry was performed.

Reservations carried from strategic alignment

  • Preserve successful mixed batches and actionable direct-skill recovery:
    mixed installs remain successful, and recovery uses an inventoried skill
    directory, retained ref and quoted POSIX-shell argument.
  • No-target must not report success: a real target-excluded zero-deployment
    install fails through the canonical outcome owner and rolls back state.

Folded in this run

  • Repaired the renamed req-tg-011 conformance binding without a waiver in
    2ef629e7a.
  • Folded all four panel follow-ups in 9fc512786: mixed-install deployed-byte
    assertions, inventory-grounded hints, argument quoting, and the documented
    dry-run exception.
  • Removed released changelog entries resurrected during main incorporation.
    Existing contributor commits are preserved; no fix(alias): reject path-traversing dependency aliases — fixes #2900 #2901 alias APIs changed.

Copilot signals reviewed

Two inline-comment reads returned no findings. The existing Copilot review is
COMMENTED, not a substitute for the required human approval.

Regression and lifecycle evidence

  • Removing the zero-deployment outcome predicate reproduced exit 0 and
    No changes in the real candidate CLI; the regression and repaired
    conformance binding failed. Guard restored.
  • Independent inventory mutation: 4 failed / 2 passed. Independent quoting
    mutation: all 3 cases failed. Guards restored.
  • Exact-head local source-CLI lifecycle: 1 passed in 3.84s, explicitly
    pinned to this worktree's .venv/bin/apm, with imported source provenance
    checked before execution, fresh HOME/APM_HOME and an unchanged 60s command
    budget. It proves failed-no-op snapshot preservation and successful mixed
    deployment with skill bytes equal to source.
  • An earlier local batch recorded 123 passes, one existing unrelated
    req-mf-016 skip, and a lifecycle timeout in 557.76s. The unchanged pinned
    quiet replay passed; resource contention is not asserted as the proven sole
    cause. A separate direct-Python invocation selected installed Homebrew
    0.30.0 and is explicitly excluded from candidate evidence.

Canonical owner and lint

Deterministic detection finds one touched owner: Install success / failure
outcome
, src/apm_cli/install/outcome.py. This extends its existing predicate;
there is no new owner or routing centralization. The registered
install-deployment-outcome boundary remains intact. Completion schema and
strict exact-head owner/functional-evidence semantics passed; the persisted
shipping status remains blocked for human approval.

The complete current CI lint contract passed locally before push: ruff pair,
YAML safety, 2100-line limit, portable relative paths, pylint R0801,
auth signals and architecture boundaries.

CI and mergeability

Current CI includes
green Linux shards, lifecycle smoke (184 passed / 1 skipped), Windows
compatibility and binary smoke. Spec conformance
and CodeQL pass.
The required gate
passes. Docs deployment is intentionally skipped on the PR; no applicable
runtime proof was replaced by that skip.

PR Head Panel stance Iterations Folds Deferred Copilot rounds CI Mergeable Merge state
#2806 9fc512786 ship_now 2 4 panel follow-ups 0 2 green MERGEABLE BLOCKED: required human review

Two outer iterations; zero CI-recovery iterations. Source, schemas, ownership,
mutation and lifecycle evidence are retained for the coordinating VP.

@danielmeppiel
Daniel Meppiel (danielmeppiel) merged commit f66538f into main Sep 9, 2026
19 checks passed
@danielmeppiel
Daniel Meppiel (danielmeppiel) deleted the bbs/fix-2796-plugin-target-noop branch September 9, 2026 11:58
Daniel Meppiel (danielmeppiel) added a commit that referenced this pull request Sep 18, 2026
…ass (#2893)

* Initial plan

* build(deps): bump setuptools to 83.0.0 in uv lockfile

Co-authored-by: sergio-sisternes-epam <207026618+sergio-sisternes-epam@users.noreply.github.com>

* Fix skills subset installs for manifestless Git collections (#2891)

* Initial plan

* Fix manifestless skill collection subset resolution

Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>

* test: restore complete architecture guard mutation coverage (#2892)

* Initial plan

* test: cover missing architecture owner guard mutations

Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>

* fix(registry): exact version selectors must match published build metadata (#2894)

* fix(registry): exact version selectors must match published build metadata

A version selector with no range operator (e.g. 1.0.2+fa163e16) was
routed through semver range matching, which ignores build metadata in
every comparison. Two published builds sharing the same
major.minor.patch (e.g. 1.0.2+fa163e16 and 1.0.2+863e11af) tied under
that comparison, so the resolver could silently return a different
build than the one requested.

Check for an exact string match against the published version list
before falling into range matching. Real ranges (^, ~, >=, wildcards)
are unaffected, since none of them can ever equal a published version
string literally.

Fixes #2877

* test(registry): give each build-metadata test its own matching tarball

Addresses review feedback: the fixture previously reused one tarball
(declared apm.yml version 1.0.2, no build metadata) for both published
VersionEntry builds. Build a distinct tarball per build whose apm.yml
version matches its VersionEntry, and return the right bytes for the
requested version, so the test stays accurate if package validation
later cross-checks the extracted version against the resolved one.

* fix(install): fail loudly when a package deploys to no target (closes #2796) (#2806)

* fix(install): fail target-excluded plugin no-ops

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

* fix: preserve agent plugin recovery refs

Addresses panel follow-ups to keep Agent Plugin target-exclusion recovery commands pinned to the selected ref, document the breaking no-op contract, and add real CLI lifecycle coverage for total no-op failure plus mixed-install success.

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

* test: update plugin target no-op lifecycle

Addresses the CI regression from the legacy exit-0 expectation by rewriting the Agent Plugin non-Copilot target test for issue #2796: a total target-exclusion no-op now fails and commits no durable state.

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

* fix: ground plugin recovery hints in declared skills

Fold #2806 advisory follow-ups: select an inventoried skill directory, quote POSIX command operands, assert mixed-install skill bytes, and document dry-run behavior. Drop release-note duplicates resurrected while incorporating main. Mutation probes reject missing outcome, inventory and quoting guards.

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

---------

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

* docs: Add WinGet method installation (#2520)

* docs: Add WinGet method installation

* docs: clarify WinGet installation and upgrades

Address review follow-ups with exact registry selection, prerequisites, separate Windows examples, and matching upgrade guidance. Keep Scoop and the contributor's additive distribution intent.

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

---------

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

* chore(deps): bump svgo from 4.0.2 to 4.1.0 in /docs (#2914)

Bumps [svgo](https://github.com/svg/svgo) from 4.0.2 to 4.1.0.
- [Release notes](https://github.com/svg/svgo/releases)
- [Commits](svg/svgo@v4.0.2...v4.1.0)

---
updated-dependencies:
- dependency-name: svgo
  dependency-version: 4.1.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(deps): preserve GitLab sparse-fetch transport (#2939)

* fix(deps): preserve GitLab sparse-fetch transport

Execute the shared transport plan using prepared remotes and per-attempt authentication. Gate REST on executed effective HTTPS, isolate live checkout identities, and add real-Git, architecture and mutation regression proof.

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

* test(spec): bind GitLab sparse fetch to port and cache requirements

Reuse the real-Git transport contract as executable evidence for req-sc-013 and req-rs-016, rather than waiving the Mode B conformance gate.

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

* docs(spec): regenerate GitLab sparse conformance evidence

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

* test(deps): align legacy REST fixtures with selected Git attempts

Configure a real strict HTTPS transport plan and a typed Git failure in REST tests, and include the new sparse-plan rule in the frozen architecture inventory.

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

* fix(deps): fold bounded sparse transport review feedback

Address CEO docs and diagnostics follow-ups on PR #2939: distinguish requested SSH from effective Git rewrites, correct packaged REST guidance, repair fallback recovery advice, and expose admitted protocol switches. Add a warning regression and isolated mutation proof without changing validation auth or other provider policy.

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

* test(deps): fold GitLab sparse Copilot regression fixes

Fix both legacy integration REST fixtures to execute a real HTTPS selector plan and raise only typed Git failures. Probe symlink capability rather than skipping Windows unconditionally, and qualify the canonical sparse-fetch credential documentation. Addresses Copilot review 5167472317 without changing validation auth or shared selector dedup semantics.

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

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

* test(deps): align clone fallback warning regression

CI recovery 1: the shared custom-port warning now correctly explains disabling all fallback configuration sources and points at the live docs route. Update its older clone consumer assertion to this reviewed wording without changing runtime behavior.

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

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

---------

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

* feat(init): onboard existing local packages without rewriting source (#2937)

* feat(init): onboard existing local packages without conversion

Build on the read-only discovery work from #2857 while keeping apply metadata-only. Prove discover, declare, install, rerun, and collision protection through the existing APMLifecycle runner.

Co-authored-by: Ron Izraeli <35386615+chkp-roniz@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* test(onboarding): bind native collision safety to local priority

Cover the existing req-pr-002 local-priority contract with the native-skill collision regression and assert its recorded diagnostic. No normative requirements or specification prose change.

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

* fix(onboarding): reject existing collisions and report successful targets

Reject conflicting pre-existing dependency slots before applying any manifest delta. Report native skill metadata from the first successful target, preserving skipped author-owned destinations. Refresh owned-collision fixtures and the architecture rule inventory.

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

* fix(onboarding): preserve global handoff and discovery boundaries

Addresses scoped panel follow-ups: preserve user scope in the separate install hint, document declined consent, and defend the existing init discovery facade and target-selection boundary without expanding admission or installation behavior.

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

---------

Co-authored-by: danielmeppiel <danielmeppiel@users.noreply.github.com>
Co-authored-by: Ron Izraeli <35386615+chkp-roniz@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* chore(deps): bump astro from 7.2.7 to 7.3.2 in /docs (#2922)

Bumps [astro](https://github.com/withastro/astro/tree/HEAD/packages/astro) from 7.2.7 to 7.3.2.
- [Release notes](https://github.com/withastro/astro/releases)
- [Changelog](https://github.com/withastro/astro/blob/main/packages/astro/CHANGELOG.md)
- [Commits](https://github.com/withastro/astro/commits/astro@7.3.2/packages/astro)

---
updated-dependencies:
- dependency-name: astro
  dependency-version: 7.3.2
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump smol-toml from 1.6.1 to 1.8.0 in /docs (#2942)

Bumps [smol-toml](https://github.com/squirrelchat/smol-toml) from 1.6.1 to 1.8.0.
- [Release notes](https://github.com/squirrelchat/smol-toml/releases)
- [Commits](squirrelchat/smol-toml@v1.6.1...v1.8.0)

---
updated-dependencies:
- dependency-name: smol-toml
  dependency-version: 1.8.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* docs: establish issue-first contribution and governance policy (#2954)

* docs: establish issue-first contribution and governance policy

Implement the approved Phase 1 scope in #2953. Separate human scope approval from automated recommendations, document the maintainer roster and progression, and preserve technical reference material in the development guide. Live automation and backlog migration remain later phases.

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

* docs: fix rendered OpenAPM specification link

Use Astro's openapm-v01 route rather than the source filename spelling. Fix the broken link reported by the Deploy Docs workflow for #2954.

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

---------

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

* fix(triage): make automated recommendations advisory (#2956)

* docs: establish issue-first contribution and governance policy

Implement the approved Phase 1 scope in #2953. Separate human scope approval from automated recommendations, document the maintainer roster and progression, and preserve technical reference material in the development guide. Live automation and backlog migration remain later phases.

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

* docs: fix rendered OpenAPM specification link

Use Astro's openapm-v01 route rather than the source filename spelling. Fix the broken link reported by the Deploy Docs workflow for #2954.

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

* fix(triage): make automated recommendations advisory

Constrain workflow outputs to classification and processing metadata, retain legacy deduplication, and preserve human scope approval in direct consumers.

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

---------

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

* docs: define issue-backed roadmap and release planning (#2959)

* docs: establish issue-first contribution and governance policy

Implement the approved Phase 1 scope in #2953. Separate human scope approval from automated recommendations, document the maintainer roster and progression, and preserve technical reference material in the development guide. Live automation and backlog migration remain later phases.

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

* docs: fix rendered OpenAPM specification link

Use Astro's openapm-v01 route rather than the source filename spelling. Fix the broken link reported by the Deploy Docs workflow for #2954.

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

* fix(triage): make automated recommendations advisory

Constrain workflow outputs to classification and processing metadata, retain legacy deduplication, and preserve human scope approval in direct consumers.

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

* docs: define issue-backed roadmap and release planning

Closes #2958. Document the approved planning model; live Project rollout remains separately gated.

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

---------

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

* fix(governance): require human scope and add neutral eligibility evidence (#2964)

* fix(governance): require human scope and add neutral eligibility evidence

Refs #2960. Keep implementation authority human, make daily docs discovery-only, and deploy compatible triage consumers with regression coverage.

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

* fix(governance): make workflow trust and manual scope boundaries explicit

Use a verified literal default-branch checkout and bind policy to its actual commit. Remove remaining docs workflow label-ratification instructions; keep companion work outside unattended runs.

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

* fix: harden governance event and child-wave boundaries

Address the six bounded review contracts: default-branch entrypoints, fresh child-wave confirmation, trusted gh resolution, complete approval records, visible references, and bounded metadata reads.

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

---------

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

* fix(git-env): skip the HTTP header probe for non-HTTP effective URLs (#2906)

* fix(git-env): skip the HTTP header probe for non-HTTP effective URLs

An `insteadOf` rule that rewrites the fetched HTTPS URL to SSH, such as
`url."git@github.com:".insteadOf = https://github.com/`, made every private
dependency download fail on the authenticated retry.

`_validated_git_url_rewrite_policy` asked whether an HTTP `extraHeader`
applied to the effective URL. With an SCP-style target that probe ran
`git config --get-urlmatch http.extraHeader git@github.com:owner/repo`,
which git rejects with `invalid URL scheme name or missing '://' suffix`
and exit status 128, so the probe raised `GitUrlRewriteProbeError`.

An HTTP header can never reach a non-HTTP transport, so the answer is
already known: report no authorization and do not spawn the probe. The
remaining non-zero branch now names the exit status instead of only
saying the probe failed.

Fixes #2898

* docs(changelog): reference the pull request number

* fix(git-env): keep malformed rewrite targets on the wrapped safety error

The non-HTTP guard added in the previous commit was the first urlsplit
applied to the effective URL, and it is evaluated as an argument to
validate_resolved_git_url_rewrite -- so before that function's
try/except. An insteadOf rule whose replacement carries unbalanced
brackets, such as url."https://[::1/".insteadOf, therefore surfaced a raw
ValueError("Invalid IPv6 URL") instead of the module's
"Unable to verify Git URL rewrite safety", and the CPython message for a
bracketed non-address embeds the host unredacted.

Guarding the scheme lookup cannot fail open: both callers pass the same
URL straight to validate_resolved_git_url_rewrite, which re-splits it
inside its try and raises the wrapped error.

* fix: complete SCP rewrite consumer coverage and recovery

Address panel follow-ups on PR #2906 with real Git config resolver and authenticated-retry regressions, HTTP origin controls, and probe-specific recovery guidance. Preserve unsafe-rule recovery for proven policy failures and synchronize authentication documentation.

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

---------

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

* docs: publish the active issue-backed roadmap (#2988)

Publish Project 2304 from the README and contribution entry points, replace pending-rollout prose, and preserve issue-owned scope and milestone-owned release planning.

Refs #2960

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

* fix(uninstall): preserve unmanaged skills with an empty inventory (#2947)

* fix(uninstall): preserve unmanaged skills with an empty inventory

* docs(changelog): link unmanaged skill cleanup fix

* test(ci): repair stale daily release smoke fixtures (#2987)

Cover the two registered owner guards, exercise GitLab REST through real transport selection, and align Windows fixtures with canonical home/path and deployed-file hash contracts. Preserve production behavior and strengthen regression witnesses for #2965.

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

* fix(cache): pin core.autocrlf=false on GitCache checkouts (#2982)

* fix(cache): pin core.autocrlf=false on GitCache checkouts (closes #2971)

Git-subpath materialization went through GitCache without the
CRLF pin that bare_cache already set, so Windows hosts with
system core.autocrlf=true recorded non-portable content_hash
values. Add a -c pin that outranks env-frozen host config,
persist it on the checkout, and rematerialize unpinned shards.

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

* docs(changelog): cite #2982 for GitCache autocrlf pin

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

* fix(cache): heal GitCache autocrlf pin under shard lock

Defer unpinned SHA-valid eviction until _create_checkout holds the
shard lock, parse the local core.autocrlf key, fail closed on land
races, and document legacy lockfile regeneration.

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

* test(cache): isolate autocrlf host fixture and fail closed

Drop inherited GIT_CONFIG_NOSYSTEM/PARAMETERS before the hostile-host
regression, assert system autocrlf=true, document lock --update recovery,
and reject unremovable unpinned shards.

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

---------

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

* chore: release v0.31.0 (#2989)

Bump pyproject.toml and the apm-cli uv.lock entry to 0.31.0, and curate the dated changelog for the minor release. Current CI lint mirror passes locally; dependency versions, public registry URLs, and hashes remain unchanged.

Post-merge: tag v0.31.0 to trigger the release workflow.

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

* feat(autopilot): origin-aware skills with CODEOWNERS-safe review (#3003)

* feat: add ORIGIN x INTENT autopilot for triage, delivery, and review

Replay the actor-aware autopilot surface onto origin/main as one
commit: unattended runs never assign; actor-session delivery assigns
the working user; standalone review requests that user as reviewer.
Schedulers own the queue scripts and fan-out; workers advise or
implement one item. Keep CODEOWNERS additive and require full
conversation context before new advice.

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

* fix(autopilot): align tags and delivery with human-scope governance

Keep triage/requested as the only request trigger. status/needs-triage
stays human state. Delivery still queues on status/accepted, then probes
scripts/governance/eligibility.cjs and requires fresh confirmation.
Unattended ORIGIN never implements.

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

* refactor(autopilot): turn monolith orchestrators into sequencing aliases

apm-issue-autopilot and batch-bug-shepherd no longer implement. They
dispatch the canonical schedulers in-session, with a hard stop so
triage advice is not implementation permission.

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

* fix(autopilot): treat FANOUT_LIMIT as concurrency, not batch size

Schedulers must persist and drain the full helper-selected list,
refilling a slot when it returns. Two slots is parallelism only.

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

* fix(autopilot): stop spam-skipping bug forms; workers own writes

Sweep markup strip no longer lets a heading eat the rest of the
body, so valid GitHub bug forms stay eligible. Issue and PR triage
workers own comments and processing labels; schedulers only queue
and fan out.

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

* feat(autopilot): add worker activation card with optional writes

Issue-triage worker declares activation_card: on. Writes default
on; write: off returns the template without commenting or labeling.

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

* feat(autopilot): move PR-review writes off the scheduler

PR-review scheduler only queues and fans out. Reviewing sessions own
comments, labels, and the actor-session @me reviewer request. The
composed worker gets an activation card with write default on.

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

* fix(autopilot): gate PR review on panel-review

Unfiltered open-PR listing queued every pull request. Fresh review
now requires the panel-review label or an explicit named list.

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

* fix(autopilot): require status/accepted before PR review

panel-review only requests a pass. Without status/accepted on the PR
or a linked issue, the reviewing session comments, clears the request
label, and stops.

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

* refactor(autopilot): rename review panel; silent unaccepted stop

Rename apm-review-panel to autopilot-pr-review-panel and keep the
old name as a compatibility alias. Scheduler, worker, and panel all
stop when status/accepted is missing. Scheduler and worker leave no
comment; panel/worker may clear panel-review.

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

* refactor(autopilot): review-worker is advisory; merge-worker is drive-to-merge

Keep autopilot-pr-review-scheduler advisory-only. It composes
autopilot-pr-review-worker and never spawn autopilot-pr-merge-worker.
Summon merge-worker by name. Old panel names stay aliases.

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

* feat(autopilot): require Autogenesis activation cards on canonical skills

Enter before work, Exit after. Do not load Autogenesis path modules.
Schedulers are write off; workers default write on.

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

* refactor(autopilot): drop Autogenesis wording from activation cards

Keep Enter/Exit cards. Do not name the source discipline.

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

* feat(autopilot): declare activation_card on for every autopilot skill

Aliases and canonical skills both set the flag. Canonical skills still
own the Enter/Exit card body.

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

* docs: add maintainer map for autopilot skills

Internal package map only. Not product documentation.

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

* refactor(autopilot): nest skill packages under packages/autopilot

Keep leaf package names. Move the maintainer map to
packages/autopilot/README.md. Point pr-description-skill deps up one
more directory. Leave .agents/skills/ deploy layout flat.

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

* feat(autopilot): require scheduler queue tables with labels

Schedulers must emit keep-set and drop-set rows (number, kind,
labels, rationale, slot) before any spawn.

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

* fix(autopilot): invocation is harness, not origin

Copilot App, local, Cloud, and Remote Agent fill actor-session.
agentic-workflow is only gh-aw / Actions.

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

* fix(autopilot): exclude completed-advice from triage sweeps

Sweep fetch skips triage/recommended and status/triaged at GitHub
so already-advised open issues and PRs are not re-listed. Do not
write status/triaged. Named requests still fetch the one item.

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

* fix(autopilot): name worker sessions Domain stage #n

Issue triage #2993, PR triage #1017, Issue delivery #2902,
PR review #2741. No GitHub title. Do not rename in-flight sessions.

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

* Keep issue-triage JSON internal, not on GitHub comments.

The public advisory is prose plus the HTML receipt. Workers still fill
schema_version 2 JSON for the parent Exit, not the issue thread.

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

* Make triage JSON receipts optional, default off.

Enter/Exit cards take json: off|on. Omitted or unknown is off, not a
missing-field stop. json: on stays an internal payload; GitHub comments
stay prose.

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

* Stop the Triage Panel AW from requiring a JSON comment tail.

Pass json: off into autopilot-issue-triage-worker. GitHub comments stay
prose plus the HTML receipt.

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

* Route the PR Review Panel AW through the review scheduler.

Load autopilot-pr-review-scheduler (write off) then run
autopilot-pr-review-worker in-thread. Do not compose the merge worker.

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

* Auto-defer PRs with no accepted issue and thank the author.

PR triage writes status/deferred unless a same-repo linked issue is
status/accepted, and asks the author to open an issue first.

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

* Queue accepted PRs for advisory review without a named list.

Unsteered PR review now unions panel-review with status/accepted on
the PR, then still applies the accepted gate and CODEOWNERS
last-comment conditions. Named list is not required when a
maintainer already accepted the PR.

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

* Deliver accepted issues even when the author is a bot.

status/accepted from a CODEOWNER is the queue gate. Author type
is not a drop reason.

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

* Remove autopilot compatibility alias stubs.

Keep the nine canonical autopilot-{domain}-{stage}-{role} packages.
Drop the 22 installable aliases, retarget tests to merge-worker and
delivery, and refresh the lockfile.

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

* docs: retarget leftover alias names to canonical autopilot skills

PRINCIPLES.md, delivery/merge worker assets, and the cut-release
eval note now name autopilot-{domain}-{stage}-{role} only. Historical
CHANGELOG entries stay as written.

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

* style: ruff-format autopilot unit tests after main merge

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

* chore: refresh lock deployment hashes after alias-name retarget

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

* fix(autopilot): align review sweep, auto-defer, and lock orphans

Copilot App queue-open now unions panel-review with status/accepted on
the PR. PR triage auto-defers only when neither the PR nor a linked
issue is accepted. Drop deleted alias packages from the lockfile and
retarget merge-worker eval fixtures.

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

* fix(lock): rewrite deployed test-coverage-expert instruction link

Install replay retargets the relative markdown link from
.github/agents to .apm/instructions. The committed copy still used
the source-relative path, which failed APM Self-Check drift.

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

* fix(autopilot): sole-assignee claim and trusted gh lookup

Actor-session delivery continues only when @me is the sole human
assignee after add, and re-checks before implement or PR. Triage
queue helpers resolve gh through get_gh_executable so a
project-controlled binary is never used.

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

---------

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

* perf(cli): lazy-load heavyweight command modules (#3001)

* perf(cli): lazy-load heavyweight command modules

Defer install, audit, pack, marketplace, uninstall, update, and prune
until the matching verb is dispatched so apm --help, apm doctor, and
apm config get no longer import those graphs.

Closes #2996

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

* docs(changelog): link lazy CLI dispatch to #3001

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

* fix(cli): freeze lazy verbs and keep completion light

PyInstaller cannot follow importlib string paths, so collect
apm_cli.commands into hiddenimports. Complete from stubs, match
stub short_help to the real command, and advertise doctor --help.

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

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Daniel Meppiel <51440732+danielmeppiel@users.noreply.github.com>

* fix(alias): reject path-traversing dependency aliases — fixes #2900 (#2901)

* fix(alias): reject path-traversing dependency aliases

Aliases were only matched against a lax character regex, letting values
like "..", "./x", or "foo/../bar" escape apm_modules at download and
integrate time. Now validating every parsed alias through
path_security's
segment checks and guarding the resolved install path inside the
download/integrate phases. Adds unit coverage for traversal and safe
aliases.

* fix(alias): surface friendly error for traversal aliases

Wrap validate_path_segments in parse_alias_override so '.'/'..' aliases
translate PathTraversalError into the existing allowed-character message
instead of leaking a low-level technical error. Strengthen traversal
tests
to assert the friendly message.

* test(alias): prove install-phase symlink escape is blocked

A valid alias like 'safe-name' passes parse-time validation, but
apm_modules_dir/safe-name can itself be a symlink pointing outside
apm_modules_dir. ensure_path_within is the only guard that resolves
symlinks before containment (download.py:65, integrate.py:622); this
test proves the escape raises PathTraversalError and never writes
outside the managed tree.

* test(alias): trap ensure_path_within guards end-to-end at install tier

The parser rejects traversal aliases (parse_alias_override), and the
ensure_path_within containment guards exist at download.py:65 and
integrate.py:622 as the defense-in-depth last line. But no test drove
those
guards through the real phase entry points -- the PR "Scenario 4" claim
(install path can never escape apm_modules even if the parser is
bypassed)
was proven only at unit tier.

Add install-tier regression traps that route malicious aliases through
the
actual phase run() functions:

- tests/red_team/install/test_alias_path_escape.py
  * download.run() rejects a '..' traversal alias and a symlink that
    resolves outside apm_modules_dir via PathTraversalError, asserting
    no
    download bytes land.
  * integrate.run() rejects the same two vectors before materialization.
  * Safe-alias controls confirm no false positive.

- tests/unit/test_registry_entry_alias_traversal.py
  * Covers the secondary parse_registry_object_entry alias validation
    (registry_entry.py:86): regex layer (%2e%2e) and
    validate_path_segments
    layer ('..', 'pkg/..'), plus a safe-alias affirmative control.

* Refactor install path handling for dependencies

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix(alias): centralize safe destinations without restricting sources

Address the PR #2901 panel and Copilot follow-ups: route all alias ingress and materialization through existing owners, reject root-equal destinations, preserve local sibling sources, and defend real reinstall metadata and hashes with regression and architecture tests. Include actionable diagnostics and migration guidance.

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

* fix(alias): preserve normalized roots and legacy cache preflight

Address round-two architecture/security and lifecycle findings. Compare both roots through path_security, give safe alias recovery guidance, and preserve existing legacy-plugin validation before alias-aware resolution can normalize cached files. Main passes the legacy missing-metadata cases; added preflight keeps that behavior. Windows prefix and legacy-preflight mutation controls fail with guards removed.

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

* fix(alias): preserve remote source anchors and cache replacement

Keep authenticated remote coordinates separate from flat aliases. Inject the existing read-only legacy cache admission at actual reuse after canonical fetch decisions, not preparation; retain same-ref failure and transactional replacement. Extend real resolver and CLI lifecycle contracts with aliased remote siblings and invalid-cache ref changes. Mutation controls detect both source-anchor regression and misplaced or missing admission.

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

* fix(alias): preserve placement and current ref replay after install

Continue the existing PR #2901 CI recovery without resetting its run. Preserve the recovered implementation and review folds: durable alias projection, contained alias scanning, specification conformance, and current-remote ref observations after successful checkout. Original contributor and follow-up commits remain in the lineage.

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

* test(cache): exercise stale bare fallback under corrupt receipts

The mutation gate showed the previous fixture passed even when receipt handling was removed because no bare-cache directory existed. Materialize the canonical shard directory so the stale-ref fallback is reachable and the regression fails without the receipt guard. Production code is unchanged.

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

* test(lockfile): declare absent alias in dev dependency fixtures

Continue CI recovery2 after Linux shard2 exposed four generic Mock dependency references whose undeclared alias attribute became another Mock. Model the real unaliased DependencyReference default explicitly instead of weakening production alias validation.

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

* fix(spec): preserve published alias schema identities

Keep existing public v0.1 schemas byte-identical and select independent v0.1.41 schemas for alias validation. Accept the new exact manifest ID through the existing contract owner while retaining old-ID compatibility and fail-closed unknowns.

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

* fix(alias): clarify compatibility and recovery after main integration

Fold specification editorial fixes, keep the unshipped security note under Unreleased, document schema opt-in compatibility, and defend actionable diagnostics with regression assertions.

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

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: danielmeppiel <danielmeppiel@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* fix(ci): repair advisory locks and platform regressions (#3021)

* fix(ci): repair advisory locks and platform regressions

Regenerate advisory workflow metadata without changing runtime pins. Preserve root-local discovery scope during scratch drift replay and make the Windows alias fixture assert the on-disk directory name. Add source-freshness and replay regression coverage.

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

* fix(ci): honor pinned workflow compiler and harden regression fixtures

Recompile PR review with gh-aw v0.87.8 and verify the generated banner against the repository pin. Set explicit package deployment defaults and assert resolver initialization, isolate symlink-only prerequisites, and prove workflow hashes normalize LF, CRLF, and CR input.

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

---------

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

* chore(deps): bump gitpython from 3.1.58 to 3.1.59 (#2921)

Bumps [gitpython](https://github.com/gitpython-developers/GitPython) from 3.1.58 to 3.1.59.
- [Release notes](https://github.com/gitpython-developers/GitPython/releases)
- [Changelog](https://github.com/gitpython-developers/GitPython/blob/main/CHANGES)
- [Commits](gitpython-developers/GitPython@3.1.58...3.1.59)

---
updated-dependencies:
- dependency-name: gitpython
  dependency-version: 3.1.59
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump js-yaml from 4.3.1 to 4.3.2 in /docs (#2920)

Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.3.1 to 4.3.2.
- [Changelog](https://github.com/nodeca/js-yaml/blob/4.3.2/CHANGELOG.md)
- [Commits](nodeca/js-yaml@4.3.1...4.3.2)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 4.3.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump devalue from 5.8.1 to 5.9.2 in /docs (#3019)

Bumps [devalue](https://github.com/sveltejs/devalue) from 5.8.1 to 5.9.2.
- [Release notes](https://github.com/sveltejs/devalue/releases)
- [Changelog](https://github.com/sveltejs/devalue/blob/main/CHANGELOG.md)
- [Commits](sveltejs/devalue@v5.8.1...v5.9.2)

---
updated-dependencies:
- dependency-name: devalue
  dependency-version: 5.9.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(build): support setuptools 83 in binary packaging

Require PyInstaller 6.17.0 and lock its minimum compatible hooks to avoid altgraph importing the removed pkg_resources module. Preserve the setuptools 83.0.0 security upgrade.

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

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sergio-sisternes-epam <207026618+sergio-sisternes-epam@users.noreply.github.com>
Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
Co-authored-by: Nadav Yogev <nadavy@jfrog.com>
Co-authored-by: danielmeppiel <danielmeppiel@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Gijs Reijn <26114636+Gijsreyn@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Ron Izraeli <35386615+chkp-roniz@users.noreply.github.com>
Co-authored-by: Arnaud <arnaudoisel@users.noreply.github.com>
Co-authored-by: Marco Frömbgen <23717573+mfroembgen@users.noreply.github.com>
Co-authored-by: Sergio Sisternes <sergio_sisternes@epam.com>
Co-authored-by: Daniel <47431549+Danvs60@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] APM install for a skill from an Agent Plugins-compatible repo is a no-op when all your targets are not Copilot

2 participants