Implemented
2026-03-17
The Ansible validator creates Python venvs containing ansible-core (and optionally Ansible collections) to perform plugin introspection (FQCN resolution, redirect detection, deprecation checks). The original implementation used a shared, hash-keyed venv directory under ~/.apme-data/collection-cache/venvs/. This had two problems:
-
Concurrency bug:
build_venvchecked for an existing directory and then created it without holding a lock. Two concurrent requests for the sameansible_core_version + collection_specshash could race, corrupting the shared venv. -
No lifecycle management: Venvs accumulated on disk indefinitely. There was no concept of sessions, TTLs, or cleanup. For future features (AI escalation via Abbenay, MCP tools for enterprise collection docstrings), we need venvs that persist for a known session duration and can be queried by other components.
Replace the shared hash-keyed venv pool with session-scoped venvs managed by VenvSessionManager, owned by the Primary orchestrator and shared read-only with validators.
Each session gets its own isolated directory with:
- A file lock (
.lock) for safe concurrent creation viafcntl.flock() - Per-core-version subdirectories with metadata (
meta.json) tracking collections, timestamps - A
venv/subdirectory within each core-version entry containing the actual virtualenv
Sessions use a client-provided session_id (VS Code workspace hash, CI job ID, stable user name). Within a session, venvs are keyed by ansible_core_version — like tox matrix entries. Collections are installed incrementally (additive, never destructive).
$SESSIONS_ROOT/
<session_id>/
<core_version>/
venv/ # full virtualenv with ansible-core + collections
meta.json # {installed_collections, created_at, last_used_at}
session.json # session-level metadata
.lock # flock target for creation serialization
The Primary orchestrator is the sole venv authority (single writer). It calls VenvSessionManager.acquire() before fanning out to validators. Validators mount the sessions volume read-only — they receive a venv_path in ValidateRequest and use it as-is. This eliminates concurrent validator writes and corruption risk.
# Ephemeral (default, backward compatible)
apme check playbook.yml
# Named session (reusable, VS Code extension use case) — planned
apme check playbook.yml --session my-project --session-ttl 7200
# Session management — planned
apme session list
apme session info my-project
apme session delete my-project
apme session reap --ttl 3600Add fcntl.flock() around build_venv calls in the existing hash-keyed scheme. This fixes the race condition but doesn't provide session lifecycle management needed for future AI escalation and MCP tools.
Rejected: Solves concurrency but not lifecycle requirements.
Use tempfile.mkdtemp() for every request, deleting when done. UV cache makes creation fast (~1-2s).
Rejected: Wasteful for repeated scans in the same session. Named sessions for VS Code extension integration require persistence.
Use an external store to coordinate venv access across processes.
Rejected: Over-engineered. File locks are sufficient for the single-host, multi-process case. No multi-host coordination needed.
- Concurrency safe: File locks prevent race conditions during venv creation
- Backward compatible: Default behavior (no
--sessionflag) creates ephemeral venvs, matching previous semantics - Future-ready: Named sessions enable VS Code extension integration and AI escalation workflows where venvs need to persist for collection docstring queries
- Observable:
session list/info/delete/reapCLI subcommands provide visibility into venv lifecycle - Atomic metadata:
os.replace()for metadata writes prevents corruption
- Disk usage: Named sessions persist until TTL expiry or explicit deletion
- fcntl dependency: Linux/macOS only (not Windows), acceptable for APME's container-first deployment
- Module-level singleton:
_managerin_venv.pymeans TTL is set on first use; subsequent calls with different TTL are ignored
src/apme_engine/collection_cache/venv_session.py— Implementationsrc/apme_engine/validators/ansible/_venv.py— Integration layertests/test_venv_session.py— 20 tests covering all lifecycle operations