Skip to content

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

Merged
Daniel Meppiel (danielmeppiel) merged 17 commits into
microsoft:mainfrom
Danvs60:fix/alias-poisoning
Sep 17, 2026
Merged

Daniel Meppiel (danielmeppiel) merged 17 commits into
microsoft:mainfrom
Danvs60:fix/alias-poisoning

Conversation

@Danvs60

@Danvs60 Daniel (Danvs60) commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

fix(alias): contain install destinations without restricting source paths

TL;DR

Reject aliases that select apm_modules itself or escape it, through the existing parsing and destination owners. Preserve safe dotted aliases, declaring-source-relative ../ chains, and authenticated same-repository siblings. Extend Daniel (@Danvs60)'s original repair with lock-alias replay and authoritative named-ref observations so older-lock replay cannot undo updated hook bytes.

Fixes #2900.

Important

Current head: 899397a1b8be577395baaad94170c7f4efff7fd9, including main at b914b1bfc5d8eaacd23a5623b157e4d7185b3580. The main merge was clean and contributor history is preserved. Exact-head local qualification and all seven hosted validation workflows passed. GitHub now reports MERGEABLE, with landing still BLOCKED by outstanding requirements.

The versioned specification label is now applied to #2900. Independent human amendment approvals and applicable GitHub review requirements remain outstanding. Tests, automated panels, and workflow authorization are not ratification.

Problem (WHY)

  • Independent raw alias joins bypassed canonical destination checks: . selected the modules root and .. its parent; equality also admitted a root-pointing symlink. The real-input phase and symlink regressions below exercise [BUG] Aliases containing ".." escape apm_modules, poisoning the lockfile identity #2900.
  • Selecting the wrong tree can read consumer metadata or hash the wrong directory. Package inventory name and version cannot substitute for dependency source identity.
  • [!] Destination validation must not redefine sources: local ../ paths stay anchored to the declaring package, and remote siblings retain authenticated repository coordinates.
  • Lost lock-alias placement and stale named refs broke replay/update lifecycles. An older lock must not replace a shared observation obtained from a successful current-remote checkout.
  • Alias constraints need independent schema publication identities, not changed bytes at already-published URLs. The schema-immutability regression now defends both original resources.

Validation follows Agent Skills' "do the work, run a validator (a script, a reference checklist, or a self-check), fix any issues, and repeat until validation passes.". The evidence below names the actual scenarios, not a line-coverage target.

Approach (WHAT)

Boundary Change
Input Reuse parse_alias_override for object, registry, and SSH/SCP ingress.
Destination Make both install phases delegate to get_install_path / build_materialization_path, with symlink-aware strict containment.
Lock and inventory Preserve optional canonical aliases and discover contained flat/dotted installs without deriving identity from inventory names.
Source and legacy reuse Keep remote source coordinates independent of placement; run existing legacy admission after choosing reuse versus replacement and before normalizing reused bytes.
Ref freshness Record a named-ref observation only after authoritative current-remote resolution and successful checkout; lock seeds cannot publish one.
Recovery Explain rename/reinstall and known-good-lock restoration, not deletion of an unsafe destination or automatic historical repair.

Implementation (HOW)

56 net changed files versus current main: 16 runtime, 22 tests, 3 static-ownership files, 7 documentation pages, 3 specification assets, 2 bundled guides, 2 conformance reports, and the changelog. Published manifest-v0.1.schema.json and lockfile-v0.1.schema.json are unchanged; historical touched-and-restored paths are not counted as net changes.

Source paths below are relative to src/apm_cli/ unless another root is shown.

Files Responsibility and review focus
models/dependency/{object_fields,reference,registry_entry}.py One alias parser across ingress; retain safe spelling, SSH userinfo, and source coordinates.
models/dependency/materialization.py, install/phases/{download,integrate}.py One strict destination authority; remove independent phase joins. Containment guard.
deps/apm_resolver.py, install/legacy_plugin_compat.py, install/phases/resolve.py Declaring-source anchoring and correctly ordered legacy-cache admission; no consumer-filesystem fallback for remote siblings.
deps/lockfile.py, commands/deps/_utils.py Optional alias serialization/replay and contained flat/dotted inventory discovery.
cache/git_cache.py, deps/{tiered_ref_resolver,github_downloader}.py Atomic per-ref observation receipts, authoritative publication, corrupt-receipt misses, and cache-byte accounting.
install/helpers/ref_reuse.py Consume the configured freshness-policy decision while preserving the existing semver owner.
models/manifest_contract.py Accept the two supported manifest schema identities; print actionable alternatives for an unknown identity without falling back silently.
.apm/architecture/owners/contracts-tooling.json, scripts/architecture_linter/checks/{contracts_test_taxonomy,transport_network_and_runtime}.py Registered guards for owner routing, source coordinates, and cache-admission order.
tests/unit/test_{alias_traversal,registry_entry_alias_traversal}.py, tests/red_team/install/test_alias_path_escape.py Real malicious names and symlinks, both phases, safe dotted controls, and Windows-prefix cases.
tests/unit/cache/test_git_cache.py, tests/unit/deps/test_{github_downloader_phase3,lockfile_consumer_contract,lockfile_field_properties,tiered_ref_resolver}.py Receipts, publication authority, lock placement, and field preservation.
tests/unit/test_{command_helpers,deps_utils,dev_dependencies}.py, tests/test_apm_resolver.py Inventory/replay helpers, explicit absent-alias mocks, and remote sibling boundaries.
tests/integration/test_{architecture_contract_guards,architecture_dependency_reference,architecture_owner_rule_mutations}.py Schema dispatch, clean-owner assertions, and executable bypass mutations.
tests/integration/test_{required_lifecycle_state_machine,transitive_chain_e2e,hook_js_sidecar_lifecycle_contract}.py Real installed-CLI replay, correct hashes/inventory, sibling chains, legacy replacement, and updated hooks surviving older-lock replay.
tests/spec_conformance/test_{alias,manifest,lockfile,resolution}_reqs.py Alias requirement coverage, canonical lock output versus permissive manifest whitespace, and old/new schema identity invariants.
docs/src/content/docs/consumer/manage-dependencies.md, docs/src/content/docs/reference/{manifest-schema,lockfile-spec}.md User-facing alias constraints, supported schema identities, and optional lock placement.
docs/src/content/docs/reference/cli/{cache,prune}.md, docs/src/content/docs/troubleshooting/migration.md Observation behavior, actual alias cleanup locations, and compatibility/recovery instructions.
docs/src/content/docs/specs/openapm-v0.1.md, docs/public/specs/manifests/openapm-v0.1.requirements.yml Proposed req-mf-025, optional lock alias, and 123 aligned requirements: 118 MUST and 5 SHOULD. Ratification remains pending.
docs/public/specs/schemas/{manifest,lockfile}-v0.1.41.schema.json, CONFORMANCE.{json,md} New publication identities and aligned conformance reporting; original published schema bytes retained.
packages/apm-guide/.apm/skills/apm-usage/{dependencies,package-authoring}.md, CHANGELOG.md Bundled author guidance and credited, explicitly breaking-invalid-input security note under Unreleased.

Latest folds: 806cd276cb7ccf1af596a93c69e92d01faaa0dfd merges current main; 899397a1b8be577395baaad94170c7f4efff7fd9 changes eight files for diagnostics, compatibility notes, two spec editorial corrections, and regression assertions. No additional runtime policy or schema constraint was introduced by these folds.

Diagrams

Legend: dashed nodes mark changed authorities; alias placement is contained independently of authenticated source coordinates.

flowchart LR
    subgraph Ingress["Alias ingress"]
        R["DependencyReference.parse"]
        G["Registry entry parser"]
        O["parse_alias_override"]
        R --> O
        G --> O
    end
    subgraph Destination["Destination authority"]
        D["Download and integrate phases"]
        P["get_install_path"]
        M["build_materialization_path"]
        C["ensure_path_within and root exclusion"]
        D --> P --> M --> C
        M --> O
    end
    subgraph Sources["Independent source coordinates"]
        S["_remote_source_paths_for_parent"]
        E["_expand_remote_parent_local_path"]
        S --> E
        E --> V["Same host, repo and ref"]
    end
    C --> A["Contained package tree"]
    A --> H["Inventory and content hash"]
    R --> S
    classDef new stroke-dasharray: 5 5;
    class O,M,C,S new;
Loading

Rendered with Mermaid CLI 11.15.0 and visually checked before publication.

Trade-offs

Warning

This is not behavior-neutral. Bare . and .. aliases are newly rejected after whitespace trimming, as are alias destinations resolving to the modules root or outside it. Explicitly pinning the new 0.1.41 manifest schema requires clients that recognize that exact identity.

  • Narrow intentional compatibility changes: no CLI command/flag rename and no lockfile-version bump. Separators and percent escapes were already invalid; .safe, safe., foo..bar, and my-skill.v2 remain supported.
  • Optional metadata is not old-client placement support: older locks without alias remain readable; older clients may preserve an unknown alias field without honoring its destination. No alias is inferred from inventory name.
  • Sources stay separate: local ../ references remain valid. Generic containment callers that permit equality are unchanged; the strict exclusion belongs to alias materialization.
  • No automatic historical repair: choose a safe alias, reinstall, and inspect metadata/artifacts. Restore a damaged lock from version control. Never delete a rejected destination. Graph-wide alias conflicts, frozen alias drift, and automatic relocation remain outside this repair.
  • Bounded evidence: installed-CLI tests use hermetic Git sources. Local qualification is macOS/CPython 3.12, not native Windows certification; hosted platform checks and human ratification are separate.

Benefits

  1. Both install phases reject modules-root and outside-root alias destinations.
  2. Safe aliases retain placement, inventory, hashes, and deployed bytes across replay.
  3. Local sibling chains and authenticated remote siblings retain source semantics.
  4. Older-lock replay cannot downgrade observations that preserve updated hook bytes.
  5. Original schema URLs retain exact bytes; unknown explicit contracts fail with separately readable supported URLs.

Validation

Exact head: 899397a1b8be577395baaad94170c7f4efff7fd9; integrated main: b914b1bfc5d8eaacd23a5623b157e4d7185b3580.

Two nonoverlapping frozen pytest invocations reported:

1128 passed, 2 skipped, 9 subtests passed in 385.22s (0:06:25)
48 passed in 1.43s

The first covers affected unit/resolver/red-team tests, the full conformance suite, architecture assertions/mutations, and the three installed-CLI lifecycle files. The second covers tests/unit/install/test_git_semver_wiring.py and tests/unit/install/phases/test_resolve_ref_resolver_reuse.py. Total: 1,176 passes plus 9 subtests, not including historical or overlapping runs.

The two existing skips concern publisher timestamp recording and an absolute-source-schema waiver; neither exempts alias handling. All 96 installed package versions match the frozen lock environment. A tooling-generated registry-URL rewrite changed no package identities and was restored; uv.lock is unchanged in this PR.

Canonical CI lint, the YAML/portable-path/2100-line predicates, duplication guard, auth boundary, and architecture boundary all passed. The version-2 owner evidence schema and strict semantic verifier cover all four detected owners with exact-head functional tests.

Mutation proof: reverting the two diagnostic improvements made both strengthened tests fail. Removing reserved-name validation and root-equality rejection caused three real-input behavioral failures plus the architecture assertion failure. All guards were restored before the passing exact-head qualification.

Documentation built 124 pages, with this output:

[+] Checked 1050 relative link(s) across generated pages. No broken relative links found.
[+] all 6 schema $id URLs resolve to byte-identical assets

The spec checklist confirms ASCII, schema/fixture parsing, 21 fixture citations, unique anchors, and agreement at all three 123-requirement count sites. Docs-sync classified the already-patched documentation as in_place_resolved; no extra advisory or companion PR is needed.

All seven exact-head hosted validation workflows succeeded: CI, Spec, CRLF, Docs, CodeQL, NOTICE, and Merge Gate. This includes both Linux shards, lifecycle/binary smoke, coverage and architecture gates, native Windows Compatibility Gate, and Linux/macOS/Windows CRLF probes. Docs deployment was correctly skipped on this PR; a build is not live publication.

The runs required routine fork-workflow authorization after verifying the active PR's exact SHA/repository/branch and unchanged workflow files. That authorization did not approve code, ratify the amendment, or enter the merge queue.

The original general/spec panels reviewed 5a0d872b9dd63b78b08923eddfad0ed1f9b369b4. This is an exact-head integration and bounded-fold qualification, not a claim that the entire panel reran or approved the new head.

Historical evidence, kept separate from current-head qualification

The prior PR body reported successful CI, Spec, CRLF, Docs, CodeQL, NOTICE, and Merge Gate workflows at 5a0d872b9dd63b78b08923eddfad0ed1f9b369b4, including CI run 34388035124. Those results and earlier recovery/mutation reports belong to their recorded SHAs; they are not current-head certification and are not added to the counts above.

Scenario Evidence

# User promise Principle(s) Test(s) proving it Type
1 Invalid aliases cannot select my modules root or escape through either phase. Secure by default tests/red_team/install/test_alias_path_escape.py::TestDownloadRejectsEscapingAlias, TestIntegrateRejectsEscapingAlias; tests/spec_conformance/test_alias_reqs.py::test_alias_rejects_symlink_destination (regression-traps for #2900) unit / integration
2 Safe aliases retain inventory, hashes, and deployed bytes; rejection preserves state. Governed by policy; DevX tests/integration/test_required_lifecycle_state_machine.py::test_required_reinstall_is_byte_idempotent_across_durable_state e2e
3 My specialized -> ../base -> ../leaf chain works with and without aliases. Portability by manifest tests/integration/test_transitive_chain_e2e.py::test_asymmetric_layout_anchors_on_declaring_pkg e2e
4 A remote alias preserves same-repository siblings, not outside or different-repository sources. Secure by default tests/test_apm_resolver.py::TestRemoteParentLocalPathFailClosed integration
5 Invalid reused caches remain unchanged; an authenticated changed-ref replacement still works. Secure by default; DevX tests/integration/test_required_lifecycle_state_machine.py::test_required_invalid_receiptless_legacy_cache_fails_with_recovery, test_required_legacy_content_hash_upgrade_preserves_skills_and_converges e2e
6 Windows-prefixed destination spellings still reject root/outside paths. Portability by manifest tests/unit/test_alias_traversal.py::TestInstallPhaseSymlinkEscape::test_extended_prefix_alias_destination unit
7 Lock replay preserves placement; inventory names do not invent aliases. Governed by policy tests/spec_conformance/test_alias_reqs.py::test_alias_survives_lock_replay_without_changing_source, test_absent_alias_is_not_inferred_from_inventory_name integration
8 Updated hooks survive older-lock replay, uninstall, and reinstall. Multi-harness support; Governed by policy tests/integration/test_hook_js_sidecar_lifecycle_contract.py::test_required_copilot_and_vscode_js_hook_lifecycle; actual receipt-byte assertion e2e
9 Corrupt receipts cannot revive stale refs, and lock seeds cannot publish fresh observations. Secure by default tests/unit/cache/test_git_cache.py::test_corrupt_remote_ref_receipt_does_not_revive_stale_bare_ref; tests/unit/deps/test_tiered_ref_resolver.py::test_only_actual_current_remote_resolution_can_publish_named_observation unit
10 Prune removes the actual alias directory and owned outputs. DevX; Governed by policy tests/integration/test_required_lifecycle_state_machine.py::test_required_dependency_prune_then_uninstall_cascades_owned_state e2e
11 Old schema pins retain bytes; unknown IDs fail with actionable supported URLs. Governed by policy; DevX tests/spec_conformance/test_alias_reqs.py::test_published_schema_content_remains_immutable; tests/integration/test_architecture_contract_guards.py::test_unknown_manifest_schema_identity_fails_closed integration
12 Invalid lock-alias recovery points to the lock and preserves rejected destinations. Secure by default; DevX tests/spec_conformance/test_alias_reqs.py::test_invalid_lock_alias_guidance_repairs_the_lock_not_the_manifest integration
13 Unchanged semver constraints replay locks; update requests fresh resolution. Governed by policy tests/unit/install/test_git_semver_wiring.py::TestMaybeResolveGitSemver::test_lockfile_replay_on_unchanged_constraint, test_fresh_resolution_when_update_refs unit

Human actions before landing

Note

Preserved maintainer receipt from the prior body, received 2026-09-09T21:30:01.242+02:00: "Authorize the one-off #2901 comment-period exception; keep all other gates." This omits only this amendment's 14-day public comment period. It is not an approval date, ratification, or a waiver of independent reviews.

  • Apply Section 9.3's versioned spec/openapm-v0.1 label to accepted issue [BUG] Aliases containing ".." escape apm_modules, poisoning the lockfile identity #2900.
  • Obtain two distinct qualified non-author amendment approvals: one implementation reviewer and one consumer/integrator reviewer, with roles and reviewed SHA recorded. Daniel authored amendment commits, so his requested review does not automatically count as independent.
  • Obtain applicable codeowner/latest-push approvals for the final head. Existing COMMENTED reviews and automated advice do not satisfy them. Requested reviewers are preserved; no review is fabricated.
  • Observe successful current-head hosted checks. The main ruleset also requires the merge queue; no merge or queue action is performed by this driver.

How to test

  • Run uv run --frozen --extra dev pytest -q tests/spec_conformance/test_alias_reqs.py tests/unit/cache/test_git_cache.py; expect placement, schema, and receipt regressions to pass.
  • Install twice with .safe, safe., foo..bar, or my-skill.v2; inspect lock alias, inventory/hash, and deployed bytes.
  • Change only the alias to . or ..; expect actionable failure without artifact changes. Restore a safe alias; never delete the rejected destination.
  • Run APM_E2E_TESTS=1 APM_RUN_INTEGRATION_TESTS=1 APM_BINARY_PATH="$PWD/.venv/bin/apm" uv run --frozen --extra dev pytest -q tests/integration/test_hook_js_sidecar_lifecycle_contract.py tests/integration/test_transitive_chain_e2e.py; expect current hook bytes and declaring-source anchors to survive replay.
  • Run bash scripts/lint-architecture-boundaries.sh, inspect current hosted checks, and complete the independent human requirements above.

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

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.
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.
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.
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.
@Danvs60

Copy link
Copy Markdown
Contributor Author

@microsoft-github-policy-service agree

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.

🟡 Changes recommended

One new registry-alias test does not actually exercise the intended validation layer, and a couple of new containment-guard call sites rely on type: ignore where a small explicit non-None guard would make the contract and types correct.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR closes a path traversal / alias poisoning hole by rejecting traversal-like dependency aliases at parse time and adding defense-in-depth containment checks when constructing alias-derived install paths during apm install.

Changes:

  • Add validate_path_segments(..., context="dependency alias") to alias parsing for object-form dependencies (including registry object entries).
  • Add ensure_path_within() containment checks at the two alias join sites in the download and integrate phases.
  • Add unit and red-team regression tests covering traversal aliases and symlink-based escapes.
File summaries
File Description
tests/unit/test_registry_entry_alias_traversal.py New unit tests targeting registry object-entry alias validation.
tests/unit/test_alias_traversal.py New unit tests for alias override parsing and ensure_path_within symlink containment.
tests/red_team/install/test_alias_path_escape.py New red-team tests driving install phases directly to ensure containment guards trip before writes.
src/apm_cli/models/dependency/registry_entry.py Add validate_path_segments check for registry entry aliases.
src/apm_cli/models/dependency/object_fields.py Add validate_path_segments check for alias overrides and map traversal rejection into ValueError.
src/apm_cli/install/phases/integrate.py Add ensure_path_within guard when alias is used to build install_path.
src/apm_cli/install/phases/download.py Add ensure_path_within guard when alias is used to build pre-download path.
Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 4
  • Review effort level: Lite

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

Comment thread tests/unit/test_registry_entry_alias_traversal.py Outdated
Comment thread src/apm_cli/install/phases/download.py Outdated
Comment thread src/apm_cli/install/phases/integrate.py Outdated
Comment thread src/apm_cli/models/dependency/object_fields.py
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@danielmeppiel

Daniel Meppiel (danielmeppiel) commented Sep 8, 2026 •

Copy link
Copy Markdown
Collaborator

APM Review Panel: needs_discussion

Published head: 58190d852693b04821d6a9d133d869ea3432a82f. Outer 4/4, Copilot 2/2 drained, CI recovery 2/3. GitHub approval-generated run_attempt=2 is not another recovery iteration. No counter reset or new outer panel occurred.

Validation base: f8df1b751efc30b32dc01b125616b51f777b4c81. Independently observed current main: d26ccdaf2fbcb29775abc94b7413790b3e32b600. Main advanced after qualification through #2806 and #2520. The current MERGEABLE result is not combined-base runtime proof; the seven successful runs and local evidence apply to published581 against the recorded validation base. Integration and requalification against the then-current main remain necessary before landing.

Driver disposition: implementation follow-ups and exact-head CI are complete on the recorded validation base; current-main integration and the resumed CEO's human specification/review reservation remain. Automated advisory agreement is not ratification or permission to merge.

Faithful9167 restores durable canonical lock-alias projection, flat/dotted alias scanning, original declaring-source anchors, cache admission after replacement choice but before reused-byte normalization, and current-remote observations after successful checkout. Source, host, repo and ref are independent of placement. c5 changes only a two-line corrupt-receipt fixture; final581 changes only one Mock alias default. Neither weakens production validation.

Reservations carried unchanged

  • "Scope validation and containment to alias destinations; preserve local transitive ../ sibling source paths and existing remote-source boundaries. Do not validate local_path as an alias."
  • "Preserve safe aliases such as .safe, safe., foo..bar and my-skill.v2; do not adopt the issue's blanket ban on dotted names."
  • "Require automated coverage for both destination joins, '.'/'..' rejection, correct metadata/hash targets, safe aliases and transitive ../ siblings. Strategic alignment is not test or code approval."
  • "Add a CHANGELOG entry and migration guidance to replace rejected aliases with safe names; explain the historical bypass without blaming sibling support or calling inventory fields canonical identity."

All four remain the review boundary: safe destinations without source restrictions; all four dotted spellings; automated behavioral/static/mutation evidence; credited, non-destructive migration guidance. No README or unrelated feature change.

Actual recovered panel

Persona Blocking Recommended Nit
python-architect 0 0 1
cli-logging-expert 0 1 0
devx-ux-expert 0 0 0
supply-chain-security-expert 0 0 0
oss-growth-hacker 0 0 0
auth-expert 0 0 0
doc-writer 0 1 0
test-coverage-expert 0 2 0
performance-expert 0 1 0

These are the nine genuine recovered returns, not a fresh fan-out. Scoped lock-error, prune-doc, receipt-size, old-lock negative-twin and L2-documentation folds are present; PR Scenario Evidence is updated. The architect's nit requests no code change. Historical reviewer effort was not recorded; all requested models were Astra. Only the interrupted CEO was resumed, explicitly Astra/high.

The four genuine specification reviewers scored 8/10; their authoritative round 1 synthesis is fold_and_ship. It does not satisfy Section 9.3. Frozen alias drift and automatic nonfrozen relocation remain deferred; graph-wide alias-conflict policy remains outside scope.

Exact evidence and failures retained

At the final head, 169 local fixture/owner regressions, all 26 affected installed-CLI lifecycles, and the complete current seven-part lint plus architecture pass. The c5 predecessor additionally recorded 465 passes plus 9 subtests, 24 touched-owner mutation cases and 247 conformance passes with two pre-existing documented waivers. These scopes are separate, not additive or relabelled as final-head runs. Current hosted CI is green: Deploy Docs: https://github.com/microsoft/apm/actions/runs/34347943524 (success); NOTICE Drift Check: https://github.com/microsoft/apm/actions/runs/34347943579 (success); Spec conformance: https://github.com/microsoft/apm/actions/runs/34347943825 (success); CodeQL: https://github.com/microsoft/apm/actions/runs/34347943857 (success); CRLF Invariance (apm#1952): https://github.com/microsoft/apm/actions/runs/34347943594 (success); CI: https://github.com/microsoft/apm/actions/runs/34347943485 (success); Merge Gate: https://github.com/microsoft/apm/actions/runs/34347943763 (success).

Actual removed guards produced 16 lock-projection failures, 14 alias/root failures, 2 stale-ref failures including actual hook-v2/v1 byte mismatch, and 2 unauthorized observation failures. All guards were restored. A corrupt-receipt test initially survived because its fixture lacked a bare-cache directory; the corrected fixture reaches the stale fallback and fails without the guard. CI1's four dev-dependency Mock failures are preserved, not hidden; CI2 corrects the fixture's absent alias to None.

Docs-impact classification is in_place_resolved. The old custom classifier and one replacement failed without filesystem tools; the owner then executed the actual classifier directly. Those failed specialist calls are not represented as successful reviews.

Outstanding landing requirements

Recovery candidate qualification and exact-head hosted CI are complete on the recorded validation base. Main subsequently advanced through #2806 and #2520; integration/requalification on then-current main is still required before landing. Human OpenAPM Section 9.3 amendment disposition remains outstanding: appropriately labelled issue, qualified nonauthor implementation and consumer/integrator approvals, and subsequent 14-day public period or an explicitly applicable human waiver. Last-push/code-owner review and merge-queue policy also remain human-owned. No AI ratification, borrowed #2818 waiver, merge, or queue admission is claimed.

Exact resumed CEO assessment, made before final-candidate validation

The historical remote failures in spec-conformance, Linux shard 1 and lifecycle checks remain the highest-signal readiness evidence; their causes are repaired in the restored candidate, not demonstrated green at its exact HEAD. The nine genuine Astra reviewer returns converge on preserving existing owners and repairing existing promises; their historical reasoning effort was not recorded. Local commit 9167a99 preserves the recovered bytes and modes over parent ae3099d, with main f8df1b7 ancestral. The scoped folds are present: lockfile-specific alias recovery guidance, flat-alias prune documentation, 40-byte per-ref receipt accounting in the existing scan with N/10N no-extra-scan coverage, the older-lock A consumer replay retaining shared receipt B before uninstall/reinstall B, and corrected L2 documentation. Receipt publication remains authorized by an exact current-remote observation and successful checkout through existing auth policy. This is restored implementation, not new acceptance evidence.

P4, P5 and P6 require destination safety without source restrictions, predictable placement and proof at the actual user boundary. The prior governed-by-policy lifecycle gap therefore outweighs opinion-only polish: its negative twin is now implemented, but final-candidate execution remains pending. Historical positive evidence in tests/integration/test_hook_js_sidecar_lifecycle_contract.py::test_required_copilot_and_vscode_js_hook_lifecycle includes the verbatim assertion assert _script(expected_marker).encode() == snapshot.file(_SCRIPT.as_posix()).content and the reviewer's 5-pass run. The independent 397 passes including 10 lifecycles, earlier broad 729 passes, and 72 post-fold passes are preserved historical diagnostics with differing scopes, not additive totals or validation of 9167a99. Final owner, mutation, native, full lint and hosted CI evidence remain pending; the remote is still ae3099d, with no new CI run. The PR body's 588-test result and Scenario Evidence remain tied to that older candidate.

The authoritative guardian round-1 synthesis records four genuine reviews at 8/10, no blocking findings, and fold_and_ship at 8/10; its short folds are present. Candidate req-mf-025 defines canonical trimming, strict contained placement and optional lock-alias projection separately from source identity, without expanding source permissions. It is not behavior-neutral errata or a ratified amendment. Section 9.3 still needs human handling: issue #2900 has only type/bug, the three human reviews at ae3099d are COMMENTED rather than APPROVED, and there is no evidence of two qualified non-author approvals, the subsequent 14-day public comment period, or a specific waiver. The #2818 waiver does not apply. P6 and P7 favor a credited, candid repair over declaring readiness from automated advisory agreement.

The restored code supports the bounded repair narrative, and the scoped reviewer recommendations are folded; this is not a request for another panel or redesign. Readiness remains unresolved because exact-head validation and the human spec-amendment process are incomplete. Continue only the authorized outer4/4, Copilot2/2-drained, CI recovery1 path after VP releases the host slot, and obtain the human amendment disposition. This advisory does not authorize merge, ratify the spec, or claim current CI is green.

Preserved previous ae3099d panel and evidence (historical, not current readiness)

APM Review Panel: ship_now

PR #2901 closes the scoped alias and cache-composition faults with passing exact-head local evidence; GitHub Actions still requires maintainer action before a human ship decision.

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

At reviewed and pushed head ae3099d, the eight active reviewers converge on no remaining substantive production finding; auth is explicitly inactive after its scope check. Both round3 counterexamples are now repaired, not waived. Remote sibling expansion obtains alias-independent source coordinates through the existing resolver/materialization authorities, retains actual-source and repository containment, and does not introduce a consumer-filesystem fallback. The independently executed tests/test_apm_resolver.py::TestRemoteParentLocalPathFailClosed::test_remote_parent_same_repo_sibling_path_expands_to_remote_virtual_dep verifies self.assertEqual(shared.dependency_ref.virtual_path, "packages/shared") and self.assertFalse(shared.dependency_ref.is_local) for the unaliased parent and all four safe aliases; outside-repository and different-clone controls remain. Legacy admission stays in its existing read-only owner and is injected after the canonical replacement decision, before normalization of reused bytes, excluding downloaded keys. In tests/integration/test_required_lifecycle_state_machine.py::test_required_invalid_receiptless_legacy_cache_fails_with_recovery, both replacement-missing-apm-yml and replacement-missing-apm-dir pass assert replaced.resolved_commit == source.commit.sha, assert replaced.content_hash == compute_package_hash(cached_package) and assert deployed.read_text(encoding="utf-8") == _skill("legacy-skill"). The seven same-ref rejection cases also pass their applicable preservation assertions, including assert_unchanged(before_cache, ArtifactSnapshot.capture(cached_package)) and _assert_same_state(before_state, after_state). These are load-bearing regression results under P4/P6, not architectural opinion. The architect's sole nit explicitly requests no change; another abstraction would not improve this bounded repair.

All four prior reservations are satisfied for the scoped repair. (1) "Scope validation and containment to alias destinations; preserve local transitive ../ sibling source paths and existing remote-source boundaries. Do not validate local_path as an alias." Closed: local original-source anchoring is unchanged, aliased/unaliased three-level local chains pass, and the repaired remote composition preserves repository coordinates and rejection boundaries. (2) "Preserve safe aliases such as .safe, safe., foo..bar and my-skill.v2; do not adopt the issue's blanket ban on dotted names." Closed: all four spellings retain lexical admission, materialization spelling, real CLI replay coverage and remote-sibling coverage; no blanket ban was substituted. (3) "Require automated coverage for both destination joins, '.'/'..' rejection, correct metadata/hash targets, safe aliases and transitive ../ siblings. Strategic alignment is not test or code approval." Closed by executed scenario evidence: independent phase guards, reserved-name ingress, actual alias-tree inventory/hash checks, preserved-state lifecycles, local chains and both newly added composition regressions are covered. I read the completed test-2901-terminal-head.log: 588 passed, 9 subtests passed in 279.68s; the complete exact-head local lint mirror also finished clean. The test reviewer independently executed 13 focused cases plus 9 subtests, all passing. Recorded pre-repair failures and restored production mutations discriminate the repaired source-coordinate route, replacement ordering and retained reuse admission; the new combined mutation produced 7 failures and removing reuse admission produced 3. These intentional failures are not current-head failures. (4) "Add a CHANGELOG entry and migration guidance to replace rejected aliases with safe names; explain the historical bypass without blaming sibling support or calling inventory fields canonical identity." Closed: the credited Security entry and non-destructive rename/unchanged-source/install/artifact-review procedure are present. The final PR body now includes Scenario Evidence, exact tests and mutations, two mmdc-validated diagrams, the pre-sibling historical bypass, the distinction between newly rejected bare dots and already-invalid slash/percent forms, and inventory-not-identity wording. I verified that the live PR body matches pr-2901-body.md. The test reviewer's sole recommended writer action is completed, not deferred; exact-head docs classification is in_place_resolved and the earlier real CDO agrees.

No further in-scope code folds or outstanding panel follow-ups remain on this evidence. This conclusion follows the repaired regressions, not the fourth-round cap, and is separate from mergeability or check policy. Evidence remains bounded: remote tests use real resolver/filesystem fixtures with a controlled download callback, not live authenticated transport; the supply-chain nondefault-host/port probe is supplementary manual evidence, not an automated regression guarantee. CLI replacement uses fixture Git sources and changes the requested ref while retaining the same resolved commit, proving replacement admission and restored artifacts rather than changed upstream content. Windows-prefix coverage is simulated, not native Windows certification. Preserve the earlier wider-run result of 2804 passed and one unrelated failure in tests/unit/install/test_install_copilot_user_instructions.py::TestCopilotUserInstructionsIntegration::test_project_scope_unaffected, whose absolute-path assertion rejects the assigned checkout's .copilot ancestor despite correct .github/instructions deployment; no unrelated fix or all-suite success is claimed. The live PR head matches Danvs60/apm:fix/alias-poisoning and the contributor commits remain retained. However, ci-2901-runs.json records six exact-head GitHub Actions runs with conclusion action_required, and ci-2901-suites.json records zero check runs for those suites; no workflow jobs executed and only CLA succeeded. The driver must therefore pause for maintainer CI action, not on unimplemented code. Local success is not GitHub CI success, and this advisory grants no approval or permission to merge.

Dissent. There is no remaining substantive specialist disagreement: the architect's nit is an explicit no-change assessment, and the test reviewer's sole recommendation has been completed and verified against the live PR body. Earlier panelist statements that exact-head verification or body writing was pending are superseded by the completed logs and posted body, not by lowering the evidence standard.

Aligned with: Portable by manifest, Secure by default, Governed by policy, Multi-harness / multi-host, OSS community-driven, Pragmatic as npm

Growth signal. Amplify the credited Security entry and single non-destructive migration procedure as a repair to APM's existing package-manager promises, not a new-feature launch or universal safety claim. The completed historical explanation protects community trust: sibling support did not introduce the bypass, only bare dot names are newly rejected, and correct inventory/hash targets do not redefine identity. No README, onboarding or broader launch work is needed.

Panel summary

Persona B R N Takeaway
CLI Logging Expert 0 0 0 Safe alias diagnostics remain actionable; cache admission now distinguishes reuse from replacement without adding output noise. No CLI-logging findings.
DevX UX Expert 0 0 0 Both composition repairs address the reproduced DevX faults; safe aliases, unchanged local sources and non-destructive errors remain covered. Exact-head verification is still pending.
Doc Writer 0 0 0 Unchanged docs match ae3099d: the bounded repairs preserve existing semantics without a new interface or inventory-as-identity claim. Final PR-body evidence remains driver-owned.
OSS Growth Hacker 0 0 0 No new growth findings. Credited release guidance and non-destructive migration remain appropriate; the bounded composition repairs need no additional onboarding or launch surface.
performance-expert 0 0 0 No actionable performance regression: reuse-only validation preserves fetch decisions; remote alias anchoring adds bounded path checks, not downloads or tree copies.
Python Architect 0 0 1 Both round3 composition faults now route through existing owners. The final focused gate passed; no remaining correctness or compounding architecture fault was found in scope.
Supply Chain Security 0 0 0 Both round3 faults are repaired at the intended boundaries; retained containment, provenance and cache-admission controls show no new scoped security concern.
Test Coverage 0 1 0 Both composition gaps have regression traps: 13 independent probes pass; exact-head gate records 588 passes plus 9 subtests. Only the owned Scenario Evidence update remains.

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

Architecture

### 1. OO / module boundaries - Before round4
```mermaid
classDiagram
    direction LR
    class DependencyReference {
      <<Dataclass>>
      +get_install_path(modules) Path
    }
    class APMPackage {
      <<Dataclass>>
      +source_path Path
      +package_path Path
    }
    class APMDependencyResolver {
      +_try_load_dependency_package()
      +_remote_repo_root_for_parent() Path
      +_expand_remote_parent_local_path()
    }
    class DownloadCallback {
      <<Protocol>>
      +__call__()
    }
    class ActivationCallback {
      <<Protocol>>
      +__call__()
    }
    class LegacyPluginCompat {
      <<IOBoundary>>
      +validate_cached_legacy_plugin()
      +upgrade_cached_legacy_plugin()
    }
    ProviderCoordinateMixin <|-- DependencyReference
    DependencyReference ..> ObjectFields : object and SSH aliases
    RegistryEntry ..> ObjectFields : alias parser
    DependencyReference ..> MaterializationPaths : destination owner
    MaterializationPaths ..> ObjectFields : defensive validation
    MaterializationPaths ..> PathSecurity : strict resolved descent
    ResolvePhase ..> MaterializationPaths : prepare destinations
    ResolvePhase ..> LegacyPluginCompat : preflight BEFORE resolver
    ResolvePhase ..> APMDependencyResolver : constructs
    APMDependencyResolver o-- DownloadCallback : injected fetch
    APMDependencyResolver o-- ActivationCallback : injected publish
    APMDependencyResolver ..> APMPackage : physical source_path
    APMDependencyResolver ..> DependencyReference : identity and destination
    APMDependencyResolver ..> PathSecurity : remote containment
    DownloadPhase ..> DependencyReference : get_install_path
    IntegratePhase ..> DependencyReference : get_install_path
    DependencySource <|-- CachedDependencySource
    DependencySource <|-- FreshDependencySource
    DependencySource <|-- LocalDependencySource
    IntegratePhase ..> DependencySource : make_dependency_source
    CachedDependencySource ..> LegacyPluginCompat : transactional upgrade
    LegacyPluginCompat ..> ResolutionStagingSession : normalize safely
    note for APMDependencyResolver "Remote repo root subtracts virtual depth from physical alias destination"
    note for ResolvePhase "Legacy preflight runs before knowing whether cached bytes will be discarded"
Loading

1. OO / module boundaries - After round4

classDiagram
    direction LR
    class DependencyReference {
      <<Dataclass>>
      +get_install_path(modules) Path
    }
    class APMPackage {
      <<Dataclass>>
      +source_path Path
      +package_path Path
    }
    class APMDependencyResolver {
      <<CallbackInjection>>
      +_try_load_dependency_package()
      +_remote_source_paths_for_parent() tuple
      +_expand_remote_parent_local_path()
    }
    class DownloadCallback {
      <<Protocol>>
      +__call__()
    }
    class ActivationCallback {
      <<Protocol>>
      +__call__()
    }
    class LegacyPluginCompat {
      <<IOBoundary>>
      +validate_cached_legacy_plugin()
      +upgrade_cached_legacy_plugin()
    }
    ProviderCoordinateMixin <|-- DependencyReference
    DependencyReference ..> ObjectFields : object and SSH aliases
    RegistryEntry ..> ObjectFields : alias parser
    DependencyReference ..> MaterializationPaths : destination owner
    MaterializationPaths ..> ObjectFields : defensive validation
    MaterializationPaths ..> PathSecurity : normalize destination AND root
    ResolvePhase ..> MaterializationPaths : prepare destinations only
    ResolvePhase ..> APMDependencyResolver : constructs and injects partial
    ResolvePhase ..> LegacyPluginCompat : binds read-only callback
    APMDependencyResolver ..> LegacyPluginCompat : invokes callback, no import
    APMDependencyResolver o-- DownloadCallback : injected fetch
    APMDependencyResolver o-- ActivationCallback : injected publish
    APMDependencyResolver ..> APMPackage : validate actual source_path
    APMDependencyResolver ..> DependencyReference : replace alias=None for coordinates
    APMDependencyResolver ..> PathSecurity : remote containment
    DownloadPhase ..> DependencyReference : get_install_path
    IntegratePhase ..> DependencyReference : get_install_path
    DependencySource <|-- CachedDependencySource
    DependencySource <|-- FreshDependencySource
    DependencySource <|-- LocalDependencySource
    IntegratePhase ..> DependencySource : make_dependency_source
    CachedDependencySource ..> LegacyPluginCompat : transactional upgrade
    LegacyPluginCompat ..> ResolutionStagingSession : normalize safely
    note for ObjectFields "Guard Clauses: one alias vocabulary; reject exactly bare dots"
    note for MaterializationPaths "Guard Clauses and Single-owner delegation: strict destination; explicit alias skips case migration"
    note for APMDependencyResolver "Dependency Injection: read-only cache callback AFTER fetch decision, BEFORE normalization; remote coordinates reuse unaliased destination owner"
    note for LegacyPluginCompat "Extract Function: one admission policy for reused resolver bytes and cached upgrade"
    class DependencyReference:::touched
    class ObjectFields:::touched
    class RegistryEntry:::touched
    class MaterializationPaths:::touched
    class ResolvePhase:::touched
    class APMDependencyResolver:::touched
    class DownloadPhase:::touched
    class IntegratePhase:::touched
    class LegacyPluginCompat:::touched
    classDef touched fill:#fff3b0,stroke:#d47600
Loading

```mermaid
### 2. Execution flow - Before round4
```mermaid
flowchart TD
    A["[LOCK] commands/install.py::install\nserialized_lifecycle_unless"] --> R["[I/O] [FS] install/phases/resolve.py::run\n_load_lockfile; _ensure_modules_dir\n_prepare_existing_materialization_paths"]
    R --> O["[I/O] models/dependency/materialization.py::prepare_materialization_path\nget_install_path -> build_materialization_path\nstrict alias destination; skip alias case migration"]
    O -->|invalid destination| E["[FS] commands/install.py: transaction.fail\napply_install_command_outcome; ctx.exit: 1"]
    O --> P{"[I/O] resolve.py::_prepare_existing_materialization_paths\ndestination.exists()?"}
    P -->|yes| V["[I/O] install/legacy_plugin_compat.py::validate_cached_legacy_plugin\npreflight before fetch decision"]
    V -->|invalid old metadata, even if replacement needed| E
    P -->|no| L
    V -->|valid or ineligible| L["[I/O] deps/apm_resolver.py::_try_load_dependency_package\nget_install_path"]
    L --> F{"[I/O] local OR missing path OR\n_should_force_recheck(dep_ref)?"}
    F -->|yes| D["[LOCK] [NET] [FS] _try_load_dependency_package reserves key\nresolve.py::download_callback\nprepare_replacement; download_package"]
    D -->|usable candidate| M
    D -->|failed required dependency| E
    F -->|no| M["[I/O] [FS] deps/apm_resolver.py\nmaterialize_marketplace_manifest\nvalidate_apm_package / APMPackage.from_apm_yml"]
    M -->|invalid| E
    M -->|valid| Q["[FS] apm_resolver.py::_activate_validated_package\nresolve.py::_activate_validated_candidate for staged bytes"]
    Q --> C{"apm_resolver.py BFS\nremote parent declares relative path?"}
    C -->|yes| S["[I/O] _remote_repo_root_for_parent\nphysical parent.source_path minus virtual depth"]
    S -->|flat alias makes root escape modules| E
    S -->|contained| H["[I/O] _expand_remote_parent_local_path\nensure_path_within(child, repo_root)\n_inherit_remote_parent_fields"]
    H -->|valid remote child| L
    H -->|outside repo| E
    C -->|local parent| LC["[I/O] _compute_dep_source_path\noriginal local source remains anchor"]
    LC --> L
    C -->|graph complete| I["[I/O] [NET] [FS] download.py::run; integrate.py::run\neach uses get_install_path\nCachedDependencySource.acquire -> upgrade_cached_legacy_plugin"]
    I --> K["[FS] install/template.py::run_integration_template\n[LOCK] phases/lockfile.py::LockfileBuilder.build_and_save"]
    K --> Z["commands/install.py\napply_install_command_outcome; ctx.exit: 0 on success"]

2. Execution flow - After round4

flowchart TD
    A["[LOCK] commands/install.py::install\nserialized_lifecycle_unless"] --> R["[I/O] [FS] install/phases/resolve.py::run\n_load_lockfile; _ensure_modules_dir\n_prepare_existing_materialization_paths"]
    R --> O["[I/O] models/dependency/materialization.py::prepare_materialization_path\nget_install_path -> build_materialization_path\nparse_alias_override; ensure_path_within both operands\nstrict alias destination; skip alias case migration"]
    O -->|invalid destination| E["[FS] commands/install.py: transaction.fail\napply_install_command_outcome; ctx.exit: 1"]
    O --> W["resolve.py::_resolve_dependencies\ninject partial(validate_cached_legacy_plugin,\nlockfile=existing_lockfile, fetched_this_run=False)"]
    W --> L["[I/O] deps/apm_resolver.py::_try_load_dependency_package\nget_install_path"]
    L --> F{"[I/O] local OR missing path OR\n_should_force_recheck(dep_ref)?"}
    F -->|yes| D["[LOCK] [NET] [FS] _try_load_dependency_package reserves key\nresolve.py::download_callback\nprepare_replacement; download_package"]
    D -->|usable path| U
    D -->|failed required dependency| E
    F -->|no| U{"apm_resolver.py:1204\ncallback present AND dedup key NOT in\n_downloaded_packages?"}
    U -->|yes: reused key| V["[I/O] install/legacy_plugin_compat.py::validate_cached_legacy_plugin\nreceiptless locked 0.28 marketplace eligibility\nmetadata, symlink and hash checks"]
    U -->|no: downloaded key or no callback| M
    V -->|invalid reused metadata| E
    V -->|valid or ineligible| M["[I/O] [FS] deps/apm_resolver.py\nmaterialize_marketplace_manifest\nvalidate_apm_package / APMPackage.from_apm_yml"]
    M -->|invalid| E
    M -->|valid| Q["[FS] apm_resolver.py::_activate_validated_package\nresolve.py::_activate_validated_candidate for staged bytes"]
    Q --> C{"apm_resolver.py BFS\nremote parent declares relative path?"}
    C -->|yes| S["[I/O] apm_resolver.py::_remote_source_paths_for_parent\ncontain actual parent.source_path within modules"]
    S --> PA{"parent_dep.alias?"}
    PA -->|yes| SA["[I/O] replace(parent_dep, alias=None).get_install_path\ncontain source-coordinate anchor within modules\nNO source copy or local-read fallback"]
    PA -->|no| SN["Use contained actual parent source anchor"]
    SA --> SR
    SN --> SR["[I/O] validate_path_segments(virtual_path)\nderive repository root by virtual depth\nensure_path_within(repo_root, modules)"]
    SR --> H["[I/O] _expand_remote_parent_local_path\nensure_path_within(parent_source, repo_root)\nensure_path_within(child, repo_root)"]
    H -->|outside repo| E
    H -->|contained sibling or repo root| IN["_inherit_remote_parent_fields\nretain host, port, repo, ref\nis_local=False; local_path=None"]
    IN --> L
    C -->|local parent| LC["[I/O] _compute_dep_source_path\noriginal local source remains anchor"]
    LC --> L
    C -->|graph complete| I["[I/O] [NET] [FS] download.py::run; integrate.py::run\neach uses get_install_path\nCachedDependencySource.acquire -> upgrade_cached_legacy_plugin\nshared validator; transactional normalization when eligible"]
    I --> K["[FS] install/template.py::run_integration_template\n[LOCK] phases/lockfile.py::LockfileBuilder.build_and_save"]
    K --> Z["commands/install.py\napply_install_command_outcome; ctx.exit: 0 on success"]
Loading

### Recommendation

Code-readiness advisory only for ae3099de229141401b770af29c32dc0f63510f9b: no further in-scope code folds remain. This stance is not approval, merge authorization or a claim of green GitHub CI. The driver terminal awaits maintainer CI action because six exact-head Actions runs require maintainer action and have no jobs; only CLA has succeeded. The maintainer must address that external CI state, starting with https://github.com/microsoft/apm/actions/runs/34231025610, then evaluate the actual required checks and make the human ship decision. Do not bypass check policy or use the fourth-round cap as a reason to ship.

---

<details>
<summary>Full per-persona findings</summary>

#### Python Architect

- **[nit]** Retain the existing owners and callback seam; no further abstraction is needed at `src/apm_cli/deps/apm_resolver.py:1204`
  Architecture assessment only, not a requested code change. The source-coordinate repair extends the resolver's existing remote-parent authority and obtains the unaliased package coordinate through DependencyReference.get_install_path. It does not invent another repository-layout algorithm. The compatibility check remains in legacy_plugin_compat.py and is injected into the resolver, so deps does not import install or duplicate legacy eligibility. The resolver retains the existing ref-drift and download decision before the new reuse check. This corrects my round3 assessment: unchanged individual owners were not sufficient to establish that alias destinations composed correctly with remote source anchoring or replacement fetches.

**Design patterns**
- Used in this PR: Guard Clauses -- ObjectFields and MaterializationPaths reject reserved aliases and non-descendant destinations before returning an install path.
- Used in this PR: Single-owner delegation -- alias ingress, install phases and remote coordinate recovery route through their existing lexical, destination and containment owners.
- Used in this PR: Dependency Injection -- ResolvePhase supplies the read-only compatibility callback to APMDependencyResolver using the established callback seam.
- Used in this PR: Extract Function -- LegacyPluginCompat shares one read-only admission function between resolver cache reuse and transactional cached upgrade.
- Pragmatic suggestion: none -- the current shape is the simplest correct design at this scope.

#### CLI Logging Expert

No findings.

#### DevX UX Expert

No findings.

#### Supply Chain Security

No findings.

#### OSS Growth Hacker

No findings.

#### Auth Expert -- inactive

Changes in src/apm_cli/deps/apm_resolver.py, src/apm_cli/models/dependency/{reference,object_fields,registry_entry,materialization}.py, src/apm_cli/install/phases/{resolve,download,integrate}.py and src/apm_cli/install/legacy_plugin_compat.py change alias destinations, source-coordinate anchoring and cache admission without changing authentication inputs, host classification, credential resolution, authorization or remote-host fallback semantics.

#### Doc Writer

No findings.

#### Test Coverage

- **[recommended]** Complete the already-owned Scenario Evidence update with final-head results.
  The supplied historical body in pr-2901-context.json has testing checkboxes but no Scenario Evidence table. This carries forward the acknowledged driver-owned writing action, not a new production defect. Read the scenario-evidence rubric and audited the full current diff. The new aliased remote-parent and invalid-cache/ref-change scenarios now have real regression traps; narrower round3 passing tests did not cover those combinations. The completed terminal-head summary is 588 passed, 9 subtests passed in 279.68s. The body must distinguish this focused result from the prior static-mutation failure, the unrelated broader-run path-sensitive failure, and unverified GitHub CI.
  *Suggested:* Map the two new composition regressions and retained alias, source-boundary, inventory/hash and no-mutation promises to user-worded scenarios, principles and real test identifiers. Mark the #2900 regression traps. Attribute red/green and mutation evidence separately from final-head execution. Preserve the owned historical corrections: only bare dots are newly rejected, slash/percent inputs were already invalid, sibling support did not introduce the bypass, and inventory fields are not canonical identity.
  *Proof (test passed):* `tests/integration/test_required_lifecycle_state_machine.py::test_required_invalid_receiptless_legacy_cache_fails_with_recovery[replacement-missing-apm-yml]` -- proves: Changing my requested ref can replace an invalid old cache and restore the installed package and deployed skills. [portability-by-manifest,secure-by-default,devx]
  `assert receipt.is_file() assert replaced.content_hash == compute_package_hash(cached_package) assert deployed.read_text(encoding="utf-8") == _skill("legacy-skill")`

</details>

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

</details>

Address the PR microsoft#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>
Preserve e845ebf and its explicit non-None contract through the phase-level RuntimeError guard. The shared materialization owner replaces the parallel alias join.

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

Daniel Meppiel (danielmeppiel) commented Sep 8, 2026 •

Copy link
Copy Markdown
Collaborator

PR #2901: engineering complete; human ratification and review remain

Published 5a0d872b9dd63b78b08923eddfad0ed1f9b369b4 on the original Danvs60/apm:fix/alias-poisoning branch. It includes authoritative main at e38261c5db4d893d6ddebc3925742e4e3bd2ba74, not the previously cached PR base. Contributor history, safe dotted aliases, declaring-package-relative ../ sources, lock placement and cache recovery are preserved.

The downstream TAG finding about changed immutable schema URLs is closed: old manifest-v0.1.schema.json and lockfile-v0.1.schema.json retain their exact public bytes; independent 0.1.41 schemas carry the alias constraints. Both known manifest IDs route through the existing contract owner; unknown IDs still fail closed. Scoped TAG and test-coverage follow-ups found no remaining in-scope issue. This is not spec ratification or live schema publication.

All seven exact-head hosted workflows succeeded: CI, Spec conformance, CRLF, Docs, CodeQL, NOTICE, Merge Gate. These are real workflow results, not a CLA-only rollup. Routine workflow authorization used standing maintainer authority after fresh exact-run trust inspection; no human PR/spec approval was supplied.

Exact-head local evidence: 605 focused passes + 9 subtests, 27 installed-CLI lifecycles, 30 owner/manifest mutation cases, and 251 conformance passes with 2 existing documented skips. All seven canonical lint gates plus architecture passed. Strict owner-v2 evidence covers all four detected owners; both known manifest IDs have additional real-loader proof. The docs build resolves 1,039 relative links and all six schema IDs to byte-identical assets. The schema correction failed five genuine pre-fix regressions before passing with the fix.

The earlier frozen-dependency block is resolved through the existing trusted enterprise feed using the frozen export and required hashes: all 95 versions match the lock and the editable package belongs only to this checkout. No dependency substitution, source borrowing, cache surgery or proxy workaround occurred.

Human actions before landing

Note

Maintainer decision, relayed by the parent shepherd and received here at 2026-09-09T21:30:01.242+02:00: "Authorize the one-off #2901 comment-period exception; keep all other gates." Only this alias amendment's 14-day public comment period is omitted. This receipt timestamp is not an approval date. Two distinct qualified non-author spec approvals, normal GitHub approvals and all technical gates remain mandatory; this exception is not ratification or any other waiver.

  1. Apply or explicitly authorize the versioned spec/openapm-v0.1 issue label required by Section 9.3. Issue [BUG] Aliases containing ".." escape apm_modules, poisoning the lockfile identity #2900 currently has only type/bug; the generic spec label is not silently treated as equivalent.
  2. Obtain two distinct qualified non-author amendment approvals, one from an implementation reviewer and one from a consumer/integrator reviewer. Record roles, final reviewed SHA and approval timestamps. Daniel authored the amendment commit; requested-reviewer status does not make him independent. Sergio plus another qualified non-author reviewer may be needed.
  3. Obtain the required codeowner and latest-push approvals for this final head. These are separate from spec ratification. The current human review snapshot has three old COMMENTED reviews and zero APPROVED reviews.

Terminal state: MERGEABLE / BLOCKED, CI green, human-gated. No engineering repair remains in this scoped handoff. Frozen alias drift, automatic alias relocation and graph-wide alias-conflict policy retain their documented scope deferrals. No merge, queue entry, release, review dismissal or approval impersonation occurred.

Historical outer4 / Copilot2 / CI-recovery2 are retained; later maintainer instructions lifted administrative holds. This continuation did not restart the completed general-panel roster or Copilot fetch. The schema correction was a scoped actual-review fold, not another CI-repair iteration. Downstream #2820 can use this exact prospective source pin, but must qualify its own head and cannot claim human-landed ancestry.

Preserved previous advisory: historical 581 and ae3099 states, before the one-off period exception; not current qualification or gates ## PR #2901 recovery advisory: human approval required

Published head: 58190d852693b04821d6a9d133d869ea3432a82f. Outer 4/4, Copilot 2/2 drained, CI recovery 2/3. GitHub approval-generated run_attempt=2 is not another recovery iteration. No counter reset or new outer panel occurred.

Validation base: f8df1b751efc30b32dc01b125616b51f777b4c81. Independently observed current main: d26ccdaf2fbcb29775abc94b7413790b3e32b600. Main advanced after qualification through #2806 and #2520. The current MERGEABLE result is not combined-base runtime proof; the seven successful runs and local evidence apply to published581 against the recorded validation base. Integration and requalification against the then-current main remain necessary before landing.

Faithful9167 restores durable canonical lock-alias projection, flat/dotted alias scanning, original declaring-source anchors, cache admission after replacement choice but before reused-byte normalization, and current-remote observations after successful checkout. Source, host, repo and ref are independent of placement. c5 changes only a two-line corrupt-receipt fixture; final581 changes only one Mock alias default. Neither weakens production validation.

At the final head, 169 local fixture/owner regressions, all 26 affected installed-CLI lifecycles, and the complete current seven-part lint plus architecture pass. The c5 predecessor additionally recorded 465 passes plus 9 subtests, 24 touched-owner mutation cases and 247 conformance passes with two pre-existing documented waivers. These scopes are separate, not additive or relabelled as final-head runs. Current hosted CI is green: Deploy Docs: https://github.com/microsoft/apm/actions/runs/34347943524 (success); NOTICE Drift Check: https://github.com/microsoft/apm/actions/runs/34347943579 (success); Spec conformance: https://github.com/microsoft/apm/actions/runs/34347943825 (success); CodeQL: https://github.com/microsoft/apm/actions/runs/34347943857 (success); CRLF Invariance (apm#1952): https://github.com/microsoft/apm/actions/runs/34347943594 (success); CI: https://github.com/microsoft/apm/actions/runs/34347943485 (success); Merge Gate: https://github.com/microsoft/apm/actions/runs/34347943763 (success).

Actual removed guards produced 16 lock-projection failures, 14 alias/root failures, 2 stale-ref failures including actual hook-v2/v1 byte mismatch, and 2 unauthorized observation failures. All guards were restored. A corrupt-receipt test initially survived because its fixture lacked a bare-cache directory; the corrected fixture reaches the stale fallback and fails without the guard. CI1's four dev-dependency Mock failures are preserved, not hidden; CI2 corrects the fixture's absent alias to None.

Final technical state: MERGEABLE / BLOCKED; all seven exact-head workflows SUCCESS, not CLA-only or an incomplete rollup. Original contributor commits and all unpublished recovery bytes/history are retained. No merge, queue entry, tag, release or branch deletion was performed.

Outstanding landing requirements: Recovery candidate qualification and exact-head hosted CI are complete on the recorded validation base. Main subsequently advanced through #2806 and #2520; integration/requalification on then-current main is still required before landing. Human OpenAPM Section 9.3 amendment disposition remains outstanding: appropriately labelled issue, qualified nonauthor implementation and consumer/integrator approvals, and subsequent 14-day public period or an explicitly applicable human waiver. Last-push/code-owner review and merge-queue policy also remain human-owned. No AI ratification, borrowed #2818 waiver, merge, or queue admission is claimed.

The original four strategic reservations remain unchanged in the existing panel comment. The actual guardian/CEO assessments and all historical failures are preserved. No additional Copilot fetch or outer iteration was started. Exact interface, requirement-ID and schema-ID handoffs were supplied to dependent owners; their own integration proof is not inherited.

Preserved previous terminal advisory (historical ae3099d state)

Shepherd driver: code complete; CI requires maintainer action

Pushed ae3099de229141401b770af29c32dc0f63510f9b to Danvs60/apm:fix/alias-poisoning, preserving the contributor's commits, including the concurrent follow-up. No merge was performed.

External stop: all six exact-head Actions runs report action_required with no workflow jobs. Only CLA has succeeded; GitHub reports MERGEABLE / BLOCKED. Start with the CI run. A maintainer must address the workflow approval/action requirement, then evaluate the actual required checks. I did not approve workflows or bypass policy.

The fourth real expert panel and CEO recommend ship_now for code readiness only, with no remaining in-scope follow-ups. The persistent panel recommendation was updated, not duplicated. All four Copilot findings are resolved: three received in-thread replies; the contributor had already resolved the fourth.

What was folded

  • Shared object/registry/SSH alias validation and one strict, symlink-aware destination authority used by both install phases.
  • Normalized Windows root comparison, safe dotted names, actionable non-destructive errors and correct alias-tree inventory/hash targets.
  • Independent remote source-coordinate anchoring, retaining same repository/ref boundaries despite flat aliases.
  • Existing legacy admission injected at actual cache reuse, after the fetch decision and before normalization; ref-change replacement remains available.
  • Credited CHANGELOG, migration, manifest/consumer and bundled guide updates; accurate historical PR body, Scenario Evidence and two validated diagrams. Synchronized docs classification: in_place_resolved.

Binding reservations and disposition

  1. "Scope validation and containment to alias destinations; preserve local transitive ../ sibling source paths and existing remote-source boundaries. Do not validate local_path as an alias." Satisfied: real three-level local chains pass aliased and unaliased; remote alias sibling tests retain origin/ref and outside/different-repository rejection.
  2. "Preserve safe aliases such as .safe, safe., foo..bar and my-skill.v2; do not adopt the issue's blanket ban on dotted names." Satisfied: all four retain spelling and pass parser, real replay and remote sibling scenarios.
  3. "Require automated coverage for both destination joins, '.'/'..' rejection, correct metadata/hash targets, safe aliases and transitive ../ siblings. Strategic alignment is not test or code approval." Satisfied by executed functional tests and real mutations, including the two composition cases discovered during review.
  4. "Add a CHANGELOG entry and migration guidance to replace rejected aliases with safe names; explain the historical bypass without blaming sibling support or calling inventory fields canonical identity." Satisfied. The bypass existed in the initial available 2025-09-18 commit; fix: harden dependency path validation #364 hardened the canonical path but missed separate aliases. feat: Plugin coexistence — apm pack --format plugin, apm init --plugin, devDependencies #379 and feat(lockfile): record installed package name + version per entry (#1888) #1904 later exposed hash/inventory consequences. fix(install): anchor transitive local_path deps on declaring package (#857) #1111/fix(deps): allow same-repo remote path deps #1732 sibling support did not introduce it. Only bare dots are newly rejected; slash/percent aliases were already invalid.

Verification and limits

  • Exact pushed head: 588 tests passed, plus 9 subtests. The full canonical local lint mirror passed, including duplication, architecture/auth boundaries and actual YAML/relative-path/2100-line predicates.
  • Real mutations failed as expected and were restored: lexical (9 failures), parser delegation (8), strict root (3), raw phase joins (11), alias selection (3), wrong hash (1), wrong metadata (1), Windows normalization plus missing admission (4), remote anchoring plus misplaced admission (7), missing actual reuse admission (3). Functional and clean-architecture assertions were both exercised.
  • The exact-head detector identifies the existing dependency identity/materialization/source-coordinate authority; functional, registered static and architecture mutation evidence is recorded.
  • A wider prior run had 2804 passes and one unchanged test's absolute-path assertion tripping over this checkout's .copilot ancestor. No unrelated test was changed. Remote resolver fixtures do not certify live transport; Windows-prefix tests are simulated. No GitHub CI-green claim.

Terminal classification: blocked solely on maintainer CI action, not on unimplemented code. Four outer iterations, two Copilot fetch/classification rounds, zero CI repair commits. No fifth review loop, auto-approval or auto-merge.

Continue the existing PR microsoft#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>
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>
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>
Preserve Danvs60's alias traversal repair and the recovered alias/cache contracts while integrating upstream main.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
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>
Daniel Meppiel (danielmeppiel) added a commit that referenced this pull request Sep 9, 2026
Preserve the original #2820 history while moving executable ownership to the published #2919 and #2923 successors. Restore current main runtime and the active v0.1 assessment verbatim; retain the exact corrective draft overlay and its release-note obligation. Final normative repin still follows qualified-human #2901 landing.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Daniel Meppiel (danielmeppiel) added a commit that referenced this pull request Sep 9, 2026
Integrates exact #2901 d1dd63c as a prospective prerequisite only. Human amendment review and landing remain outstanding. Original local/audit and aggregate implementation work stays in #2919 and #2923.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Daniel Meppiel (danielmeppiel) added a commit that referenced this pull request Sep 9, 2026
Integrate exact prospective #2901 5a0d872. Human amendment approval and landing remain outstanding.

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

Copy link
Copy Markdown
Collaborator

PR #2901 follow-up: integrated and qualified; human approvals remain

Pushed 806cd276cb7ccf1af596a93c69e92d01faaa0dfd (clean merge of current main) and 899397a1b8be577395baaad94170c7f4efff7fd9 (eight-file diagnostic/documentation fold) directly to Danvs60/apm:fix/alias-poisoning, preserving contributor history. GitHub now reports MERGEABLE, not the previous conflict. All seven exact-head validation workflows passed, including CI, native Windows Compatibility Gate, Spec, and Merge Gate.

Breaking-change assessment: intentionally rejects previously accepted bare ./.. aliases and unsafe symlink destinations. Safe dotted aliases and local ../ source paths remain supported. No CLI/flag rename or lockfile-version bump. Explicit 0.1.41 manifest schema pins require supporting clients; preserving an unknown lock alias on an older client is not placement support.

Regression evidence: 1,176 local passes plus 9 subtests across two nonoverlapping frozen runs, with two existing conformance skips. Coverage includes both install phases, real symlinks, lock placement, inventory/hash consistency, local/remote siblings, legacy replacement, and actual hook bytes plus receipt bytes across older-lock replay. Reverting both improved messages made the strengthened tests fail; removing alias guards also broke the behavioral and architecture assertions. All guards were restored. Exact-head functional evidence covers all four detected canonical owners.

The PR body now maps all 56 net files by responsibility and each user promise to its test. The latest fold clarifies non-destructive recovery and schema compatibility, restores the unshipped changelog entry to Unreleased, and fixes the spec's portable note and amendment section list. Published original schema bytes remain unchanged. The original general/spec panels are historical-head advice, not a new final-head approval.

Human requirements: spec/openapm-v0.1 is now applied to accepted issue #2900. Two distinct qualified non-author amendment approvals, including implementation and consumer/integrator perspectives, are still needed. Applicable codeowner/latest-push approval and merge-queue requirements also remain. The existing one-off comment-period exception is preserved without extending it. No review was fabricated, no reviewer changed, and no merge/queue action was taken.

@danielmeppiel
Daniel Meppiel (danielmeppiel) merged commit 2709b9c into microsoft:main Sep 17, 2026
25 checks passed
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] Aliases containing ".." escape apm_modules, poisoning the lockfile identity

3 participants