fix: harden global lifecycle state handling - #2655
fix: harden global lifecycle state handling#2655Daniel Meppiel (danielmeppiel) merged 23 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Hardens global (user-scope) lifecycle state correctness by (1) making audit/CI checks understand deployment roots outside ~/.apm, (2) stabilizing Hermes as an explicit-only target (not experimental-flag gated), (3) tightening MCP stale-server cleanup to fail closed and preserve user-owned config, and (4) serializing workspace mutations with a cross-process lifecycle lock.
Changes:
- Add a cross-process lifecycle lock and apply it to most CLI state-mutating commands; make install transactions acquire/release the same lock safely across error paths.
- Fix global audit/drift to resolve and compare against external deployment roots (e.g.,
~/.agents,~/.hermes,~/.claude) without mutating those roots. - Make Hermes stable explicit-only (remove experimental flag gating) and extend MCP cleanup to cover Hermes YAML and Antigravity’s user-scope config location.
Reviewed changes
Copilot reviewed 56 out of 56 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/test_windows_compat_gate_workflow.py | Updates Windows compat gate contract to include one integration lock test path. |
| tests/unit/test_uninstall_engine_helpers.py | Adds failure-mode regression tests ensuring MCP cleanup failures don’t drop lock ownership. |
| tests/unit/test_uninstall_dev_dependencies.py | Updates expected uninstall output to reflect fail-closed MCP cleanup behavior. |
| tests/unit/test_prune_command.py | Adds prune regression test for MCP cleanup failure preserving lock/config bytes. |
| tests/unit/test_mcp_integrator_remove_stale.py | Adds coverage for atomic-failure preservation, Hermes YAML cleanup, and strict malformed shapes. |
| tests/unit/test_cli_consistency.py | Updates compile help assertions for explicit-only Hermes exclusion from all. |
| tests/unit/integration/test_targets.py | Updates Hermes target invariants and removes experimental-flag gating. |
| tests/unit/integration/test_antigravity_target.py | Adds Windows-compat atomic-write/mode preservation coverage for Antigravity MCP config. |
| tests/unit/install/test_workspace_locking.py | Unit contracts for lifecycle lock behavior and error-path release. |
| tests/unit/install/test_install_transaction.py | Adds multi-process serialization tests + interruption/abandonment/repeat-completion lock release coverage. |
| tests/unit/core/test_target_resolution_v2.py | Updates target filtering expectations to keep stable Hermes. |
| tests/unit/core/test_target_detection.py | Removes Hermes from experimental target set characterization. |
| tests/unit/core/test_target_catalog.py | Updates target-set characterization and Hermes profile expectations. |
| tests/unit/commands/test_uninstall_atomic_selection.py | Adds uninstall regression test ensuring MCP cleanup failure preserves lock/config bytes. |
| tests/integration/test_mcp_install_flow.py | Adds global manifest removal + prune flows validating Antigravity MCP cleanup semantics. |
| tests/integration/test_lifecycle_workspace_lock.py | Cross-command subprocess regression test ensuring lifecycle serialization across commands/scope. |
| tests/integration/test_hermes_target.py | Updates Hermes E2E tests for stable explicit-only behavior and MCP selection changes. |
| tests/integration/test_global_audit_deploy_root.py | New integration coverage: global audit resolves external roots read-only and detects drift. |
| src/apm_cli/policy/ci_checks.py | Resolves deployed-file paths for global scope and audits external target roots read-only. |
| src/apm_cli/integration/targets.py | Documentation tweak reflecting Hermes stable explicit-only status. |
| src/apm_cli/integration/mcp_integrator.py | Adds strict config-shape validation, atomic writes, and Hermes YAML MCP stale cleanup. |
| src/apm_cli/integration/mcp_integrator_install.py | Removes Hermes from auto-discovery/experimental opt-in gating. |
| src/apm_cli/install/transaction.py | Makes install transactions acquire/release lifecycle lock safely across failure paths. |
| src/apm_cli/install/phases/targets.py | Removes Hermes experimental enable-hint gate. |
| src/apm_cli/install/locking.py | New lifecycle locking module (FileLock-based) + decorator for command serialization. |
| src/apm_cli/install/drift.py | Adds user-scope replay and external-root drift comparisons with absolute-claim rebasing. |
| src/apm_cli/install/audit_target_roots.py | New helpers to project external target roots into audit scratch + rebase claims. |
| src/apm_cli/install/audit_replay.py | Threads user-scope into CI audit replay preparation and target resolution. |
| src/apm_cli/core/target_detection.py | Adds Hermes to accepted target vocabulary and updates description text. |
| src/apm_cli/core/target_catalog.py | Marks Hermes capability as explicit-only rather than experimental-flag gated. |
| src/apm_cli/core/scope.py | Adds helper to resolve workspace deploy root for user-scope metadata workspaces. |
| src/apm_cli/core/experimental.py | Removes Hermes experimental flag registration. |
| src/apm_cli/commands/update.py | Serializes apm update via lifecycle lock. |
| src/apm_cli/commands/uninstall/engine.py | Broadens stale MCP server set derivation and fails closed on cleanup write errors. |
| src/apm_cli/commands/uninstall/cli.py | Serializes uninstall and fails closed when MCP cleanup fails (prevents persisting dropped ownership). |
| src/apm_cli/commands/prune.py | Serializes prune and integrates MCP cleanup into prune-before-lock-write flow. |
| src/apm_cli/commands/plugin/init.py | Serializes plugin init via lifecycle lock. |
| src/apm_cli/commands/marketplace/plugin/remove.py | Serializes marketplace plugin remove via lifecycle lock. |
| src/apm_cli/commands/marketplace/plugin/add.py | Serializes marketplace plugin add via lifecycle lock. |
| src/apm_cli/commands/marketplace/init.py | Serializes marketplace init via lifecycle lock. |
| src/apm_cli/commands/marketplace/init.py | Serializes marketplace add/update/remove via lifecycle lock. |
| src/apm_cli/commands/lock.py | Serializes lock command execution via lifecycle lock. |
| src/apm_cli/commands/install.py | Serializes install and hardens root-redirect/transaction teardown ordering. |
| src/apm_cli/commands/init.py | Serializes project init via lifecycle lock. |
| src/apm_cli/commands/deps/cli.py | Serializes deps clean/update via lifecycle lock. |
| src/apm_cli/commands/approve.py | Serializes approve/deny via lifecycle lock. |
| src/apm_cli/adapters/client/gemini.py | Switches Gemini config writes to atomic writes with secure permissions. |
| docs/src/content/docs/reference/targets-matrix.md | Updates Hermes status and compilation guidance in targets matrix docs. |
| docs/src/content/docs/reference/manifest-schema.md | Reclassifies Hermes as stable in manifest schema docs. |
| docs/src/content/docs/reference/environment-variables.md | Updates HERMES_HOME semantics for explicit-only Hermes. |
| docs/src/content/docs/reference/cli/experimental.md | Removes Hermes from experimental-flag list. |
| docs/src/content/docs/reference/cli/deps.md | Updates deps target docs for Hermes stable explicit-only behavior and all exclusion. |
| docs/src/content/docs/producer/compile.md | Removes Hermes from experimental-target list in compile docs. |
| docs/src/content/docs/integrations/hermes.md | Updates Hermes integration page to stable explicit-only and MCP behavior. |
| docs/astro.config.mjs | Renames Hermes nav label to non-experimental. |
| .github/workflows/ci.yml | Expands Windows compat gate to include lifecycle subprocess integration contract. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
@microsoft-github-policy-service agree |
7658e73 to
d5cbf05
Compare
Vanilla apm has no lifecycle serialization of its own (microsoft/apm#2655), so concurrent global installs corrupt the shared workspace.
|
@.shepherd-advisory-2655.md |
b82f5fa to
227d6ca
Compare
|
Rebased onto current main at c919ba3 from b82f5fa -> 227d6ca. Conflicting paths resolved (faithful merge of both intents):
Lint contract: `uv run --extra dev ruff check src/ tests/ scripts/lint_architecture_boundaries.py scripts/architecture_linter/` and `uv run --extra dev ruff format --check src/ tests/ scripts/lint_architecture_boundaries.py scripts/architecture_linter/` both passed post-rebase; the same pair also passed in --quiet mode with no output. Post-push mergeability: `gh pr view --json mergeStateStatus,mergeable` reports `BLOCKED / MERGEABLE`; BLOCKED is the expected fork workflow approval/review gate, not a conflict. Push used `git push --force-with-lease=fix/omni-apm-blockers:b82f5fa8eb3e74472b0cb64a0b3dfba61e120d4e lkshrk-fork HEAD:fix/omni-apm-blockers` (--force-with-lease, never bare --force). Fork workflows pending approval for this head: CI 33769287059, Spec conformance 33769287062, Deploy Docs 33769287068, NOTICE Drift Check 33769287091, CodeQL 33769287175, Merge Gate 33769287178, CRLF Invariance 33769287531. Ready for maintainer review. |
fa62a20 to
ac7529a
Compare
|
Rebased onto current main at 354faad -> ac7529a. Conflicting paths resolved (faithful merge of both intents):
Regression-trap test re-verified post-rebase (mutation-break gate):
Lint contract: Post-push mergeability: Spec waiver preserved: Fork workflows pending approval for this head: CI 33806493037, Spec conformance 33806492140, Deploy Docs 33806492235, NOTICE Drift Check 33806492251, CodeQL 33806492187, Merge Gate 33806492160, CRLF Invariance 33806492236. Ready for maintainer review. |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Scope MCP cleanup by runtime, audit bounded external roots, and serialize remaining lifecycle mutators. Add dual-guardrail architecture evidence and regression coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep the install command under its enforced module-size budget while preserving transaction cleanup when root restoration fails. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Freeze the new architecture rule, preserve platform-visible modes on Windows, and keep the expanded Windows family explicitly bounded. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reject symlinked MCP configs, route prune cleanup to project scope, report lock contention, bound external scans, and align Hermes guidance and help. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Serialize watch startup, decode target-native audit claims, preserve partial MCP ownership, and harden cleanup/docs per the terminal review findings. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update the exact help assertion for the Hermes runtime added by the final review fold and exposed by CI shard 1. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Serialize remaining state writers, reject symlinked lifecycle locks, add cross-process regression coverage, and correct the converged user guidance. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Allow slow-starting audit and lifecycle contender commands to prove they remain blocked by the lifecycle lock instead of failing on CLI startup latency. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fail closed when governed external target files become unsafe symlinks, avoid lifecycle-lock contention for read-only cleanup previews, and align Hermes documentation with HERMES_HOME. Addresses apm-review-panel follow-ups from supply-chain-security, performance-expert, doc-writer, and oss-growth-hacker. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: lkshrk <5067446+lkshrk@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: lkshrk <5067446+lkshrk@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
apm-spec-waiver: Corrects existing adapter ownership behavior without changing the OpenAPM contract. Co-authored-by: lkshrk <5067446+lkshrk@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
7535808 to
aba8d6e
Compare
|
Rebased onto current main at 1560d88 -> aba8d6e. Pre-rebase head was 7535808. Conflicting paths resolved (faithful merge of both intents):
Regression-trap test re-verified post-rebase (mutation-break gate):
Lint contract: Post-push mergeability: Spec waiver preserved: Fork workflows pending approval for this head: Deploy Docs 33816499428, CRLF Invariance 33816499156, CodeQL 33816499183, Spec conformance 33816499190, NOTICE Drift Check 33816499328, Merge Gate 33816499297, CI 33816499308. Ready for maintainer review. |
Bring the branch up to origin/main after the microsoft#2745 and microsoft#2776 merges, and keep drift.py within the install module LOC budget. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Bring the branch up to the latest origin/main and preserve both the MCP/LSP --only documentation and Hermes explicit-only target semantics. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Bring the branch up to the newest origin/main and keep both uninstall cleanup regression tests after the hook cleanup outcome changes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
ccbe5d2 to
56c2d81
Compare
5d6fc94
into
microsoft:main
* fix: harden global agent lifecycle * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * fix: address lifecycle review findings * fix: fold lifecycle hardening review findings Scope MCP cleanup by runtime, audit bounded external roots, and serialize remaining lifecycle mutators. Add dual-guardrail architecture evidence and regression coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * refactor: extract install context finalization Keep the install command under its enforced module-size budget while preserving transaction cleanup when root restoration fails. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: align CI contracts with lifecycle coverage Freeze the new architecture rule, preserve platform-visible modes on Windows, and keep the expanded Windows family explicitly bounded. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: fold terminal lifecycle review findings Reject symlinked MCP configs, route prune cleanup to project scope, report lock contention, bound external scans, and align Hermes guidance and help. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: fold final lifecycle and audit findings Serialize watch startup, decode target-native audit claims, preserve partial MCP ownership, and harden cleanup/docs per the terminal review findings. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: align global MCP help contract Update the exact help assertion for the Hermes runtime added by the final review fold and exposed by CI shard 1. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: close terminal lifecycle review gaps Serialize remaining state writers, reject symlinked lifecycle locks, add cross-process regression coverage, and correct the converged user guidance. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: stabilize lifecycle lock startup wait Allow slow-starting audit and lifecycle contender commands to prove they remain blocked by the lifecycle lock instead of failing on CLI startup latency. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: fold lifecycle audit panel followups Fail closed when governed external target files become unsafe symlinks, avoid lifecycle-lock contention for read-only cleanup previews, and align Hermes documentation with HERMES_HOME. Addresses apm-review-panel follow-ups from supply-chain-security, performance-expert, doc-writer, and oss-growth-hacker. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: fold final review followups Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: restore audit symlink compatibility Co-authored-by: lkshrk <5067446+lkshrk@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: preserve lifecycle LOC budget exception Co-authored-by: lkshrk <5067446+lkshrk@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * chore: restore spec conformance waiver context apm-spec-waiver: Corrects existing adapter ownership behavior without changing the OpenAPM contract. Co-authored-by: lkshrk <5067446+lkshrk@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test: align MCP cleanup failure aggregation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * style: format compile conflict resolution Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix CI drift after main merge Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add lifecycle coverage for global audit invariants Co-authored-by: Copilot <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 <223556219+Copilot@users.noreply.github.com> Co-authored-by: lkshrk <5067446+lkshrk@users.noreply.github.com>
Summary
Fixes four global lifecycle correctness issues discovered during Omni's APM hard migration:
Closes #2608.
Safety properties
Scenario Evidence
uv run --extra dev pytest tests/integration/test_lifecycle_workspace_lock.py tests/integration/test_architecture_lifecycle_lock.py -quv run --extra dev pytest tests/integration/test_mcp_install_flow.py tests/unit/test_mcp_integrator_remove_stale.py tests/unit/test_uninstall_engine_helpers.py -quv run --extra dev pytest tests/integration/test_global_audit_deploy_root.py -qallwhile remaining selectable by manifest or--target.uv run --extra dev pytest tests/integration/test_hermes_target.py tests/unit/core/test_target_catalog.py tests/unit/core/test_target_detection.py tests/unit/core/test_target_resolution_v2.py -qValidation