Summary
When deployed via Docker (Dockerfile.swarm.dashboard + docker-compose.yml, which install Python deps with uv sync --no-dev into a venv at /workspace/.venv), several core features are broken out of the box. The common root cause for most of them: code paths invoke the system python3 instead of the venv interpreter, and the system interpreter has none of the project dependencies.
Environment: @evoapi/evo-nexus v0.32.3, dashboard image built from Dockerfile.swarm.dashboard, single-container deploy via docker-compose.yml, Claude Code authenticated via a long-lived OAuth token (CLAUDE_CODE_OAUTH_TOKEN from claude setup-token).
Note: /workspace/.venv/bin/python is a symlink to the system python3, but invoking it via the venv path activates the venv site-packages (through the adjacent pyvenv.cfg). Invoking bare python3 does not.
Bug 1 — Claude Code hooks crash every turn (Stop hook error occurred) — critical
dashboard/backend/claude_hook_bootstrap.py writes the hook command from:
DISPATCHER_CMD_TEMPLATE = (
'python3 "$CLAUDE_PROJECT_DIR/dashboard/backend/claude_hook_dispatcher.py" {event}'
)
claude_hook_dispatcher.py does an unconditional module-top import yaml. Under the Docker/uv install the system python3 lacks yaml, so every PreToolUse/PostToolUse/Stop/SubagentStop hook dies with ModuleNotFoundError: No module named 'yaml', surfaced to the user as a Stop hook error occurred chat notification on every agent turn.
Repro
docker compose exec -T -w /workspace dashboard sh -c \
'echo "{}" | CLAUDE_PROJECT_DIR=/workspace python3 dashboard/backend/claude_hook_dispatcher.py Stop'
# => ModuleNotFoundError: No module named 'yaml'
Two-part fix (both required):
- Use the venv interpreter in the template:
DISPATCHER_CMD_TEMPLATE = (
'"$CLAUDE_PROJECT_DIR/.venv/bin/python" '
'"$CLAUDE_PROJECT_DIR/dashboard/backend/claude_hook_dispatcher.py" {event}'
)
- Make
run() reconcile existing managed entries. It is currently idempotent-by-presence on the _evonexus_managed sentinel (if not _has_managed_hook(entries): append), so it never rewrites a stale command already baked into settings.json. It should compare each managed hook's command against the current template and overwrite in place on mismatch (preserving non-managed entries such as agent-tracker.sh).
Also affects claude_hook_dispatcher.py::_runner_cmd, which launches plugin .py handlers with ["python3", handler_path] under a stripped env (no PYTHONPATH/VIRTUAL_ENV) — same failure once a plugin handler imports any non-stdlib dep.
Bug 2 — db-mysql / db-mongo / db-redis skills are non-functional (undeclared drivers) — high
These skills import pymysql / pymongo / redis (and pymongo needs dnspython for the documented mongodb+srv:// Atlas URIs), but none are declared in pyproject.toml / uv.lock, so they are absent from the venv and every test/query returns driver_missing. Only db-postgres works, and only because psycopg2-binary is pulled in transitively.
The SKILL.md examples additionally instruct python3 ...db_client.py (system interpreter) and uv pip install <driver> "on first use" — the latter is ephemeral and lost on every image rebuild.
Fix: declare redis, pymongo, dnspython, pymysql in pyproject.toml; change the SKILL.md examples to uv run python ....
Bug 3 — Recurring scheduler never runs in the Docker deployment — high
The active docker-compose.yml omits the scheduler service that the sibling docker-compose.hub.yml / .proxy.yml / evonexus.stack.yml include, and start-dashboard.sh (the container entrypoint's CMD) launches only the terminal-server and Flask — never scheduler.py. Result: every recurring ADW routine (good_morning, end_of_day, memory_sync, memory_lint, backup, and config/routines.yaml) silently never fires.
Knock-on: plugin routine activation is permanently stuck at routine_activation_pending because plugin_loader expects a scheduler.pid that never exists.
Repro
docker compose exec -T dashboard sh -c \
'for p in /proc/[0-9]*; do tr "\0" " " < $p/cmdline; echo; done' | grep scheduler.py
# => (no output)
Fix: ship a scheduler in the default compose. Note two subtleties if done as a separate service: (a) plugin_loader's SIGHUP-based hot-reload cannot cross container PID namespaces, and (b) the sibling composes mount ADWs/logs as a named volume the dashboard container cannot see (it uses a host bind), so scheduler.pid is invisible cross-container. Launching scheduler.py as a sibling process inside the dashboard container (same PID namespace + same ADWs/logs mount) sidesteps both. Either way it must use uv run python / the venv, never bare python3.
Bug 4 — REQUIRE_ANTHROPIC_KEY gate ignores OAuth token — medium
entrypoint.sh loops forever waiting only for ANTHROPIC_API_KEY:
while [ -z "${ANTHROPIC_API_KEY:-}" ]; do ... sleep 30 ... done
A deployment authenticated via CLAUDE_CODE_OAUTH_TOKEN (from claude setup-token) has no ANTHROPIC_API_KEY, so any service with REQUIRE_ANTHROPIC_KEY=1 (e.g. the scheduler/telegram services in the sibling composes) hangs forever.
Fix:
while [ -z "${ANTHROPIC_API_KEY:-}" ] && [ -z "${CLAUDE_CODE_OAUTH_TOKEN:-}" ]; do ...
Bug 5 — Scheduler UI suggests make run R=<id> but the image has no make — low
dashboard/backend/routes/scheduler.py emits a command field of the form make run R=<id> (surfaced as a copy-pasteable command in Scheduler.tsx), but the python:3.12-slim-based runtime image never installs make, so the suggested command fails with make: not found.
Fix: emit uv run python ADWs/routines/<script> (or install make).
Bug 6 — npx updater cannot update a Docker/compose install — low
The updater does git merge --ff-only, which aborts on any locally-modified tracked file, and its restart path targets a systemd unit / start-services.sh that don't exist in a Docker deployment. So a compose-based install can't be updated through it.
Suggested consolidated direction
Introduce a single interpreter resolver used everywhere a routine/hook/handler is launched — prefer the absolute venv path $WORKSPACE/.venv/bin/python when it exists, fall through to uv run python, never bare python3 — and declare all runtime drivers in pyproject.toml. I've applied all of the above locally and confirmed each fix; happy to open a PR if useful.
Summary
When deployed via Docker (
Dockerfile.swarm.dashboard+docker-compose.yml, which install Python deps withuv sync --no-devinto a venv at/workspace/.venv), several core features are broken out of the box. The common root cause for most of them: code paths invoke the systempython3instead of the venv interpreter, and the system interpreter has none of the project dependencies.Environment:
@evoapi/evo-nexusv0.32.3, dashboard image built fromDockerfile.swarm.dashboard, single-container deploy viadocker-compose.yml, Claude Code authenticated via a long-lived OAuth token (CLAUDE_CODE_OAUTH_TOKENfromclaude setup-token).Note:
/workspace/.venv/bin/pythonis a symlink to the systempython3, but invoking it via the venv path activates the venvsite-packages(through the adjacentpyvenv.cfg). Invoking barepython3does not.Bug 1 — Claude Code hooks crash every turn (
Stop hook error occurred) — criticaldashboard/backend/claude_hook_bootstrap.pywrites the hook command from:claude_hook_dispatcher.pydoes an unconditional module-topimport yaml. Under the Docker/uv install the systempython3lacksyaml, so everyPreToolUse/PostToolUse/Stop/SubagentStophook dies withModuleNotFoundError: No module named 'yaml', surfaced to the user as aStop hook error occurredchat notification on every agent turn.Repro
Two-part fix (both required):
run()reconcile existing managed entries. It is currently idempotent-by-presence on the_evonexus_managedsentinel (if not _has_managed_hook(entries): append), so it never rewrites a stale command already baked intosettings.json. It should compare each managed hook'scommandagainst the current template and overwrite in place on mismatch (preserving non-managed entries such asagent-tracker.sh).Also affects
claude_hook_dispatcher.py::_runner_cmd, which launches plugin.pyhandlers with["python3", handler_path]under a stripped env (noPYTHONPATH/VIRTUAL_ENV) — same failure once a plugin handler imports any non-stdlib dep.Bug 2 —
db-mysql/db-mongo/db-redisskills are non-functional (undeclared drivers) — highThese skills import
pymysql/pymongo/redis(andpymongoneedsdnspythonfor the documentedmongodb+srv://Atlas URIs), but none are declared inpyproject.toml/uv.lock, so they are absent from the venv and every test/query returnsdriver_missing. Onlydb-postgresworks, and only becausepsycopg2-binaryis pulled in transitively.The
SKILL.mdexamples additionally instructpython3 ...db_client.py(system interpreter) anduv pip install <driver>"on first use" — the latter is ephemeral and lost on every image rebuild.Fix: declare
redis,pymongo,dnspython,pymysqlinpyproject.toml; change theSKILL.mdexamples touv run python ....Bug 3 — Recurring scheduler never runs in the Docker deployment — high
The active
docker-compose.ymlomits theschedulerservice that the siblingdocker-compose.hub.yml/.proxy.yml/evonexus.stack.ymlinclude, andstart-dashboard.sh(the container entrypoint's CMD) launches only the terminal-server and Flask — neverscheduler.py. Result: every recurring ADW routine (good_morning,end_of_day,memory_sync,memory_lint,backup, andconfig/routines.yaml) silently never fires.Knock-on: plugin routine activation is permanently stuck at
routine_activation_pendingbecauseplugin_loaderexpects ascheduler.pidthat never exists.Repro
Fix: ship a scheduler in the default compose. Note two subtleties if done as a separate service: (a)
plugin_loader's SIGHUP-based hot-reload cannot cross container PID namespaces, and (b) the sibling composes mountADWs/logsas a named volume the dashboard container cannot see (it uses a host bind), soscheduler.pidis invisible cross-container. Launchingscheduler.pyas a sibling process inside the dashboard container (same PID namespace + sameADWs/logsmount) sidesteps both. Either way it must useuv run python/ the venv, never barepython3.Bug 4 —
REQUIRE_ANTHROPIC_KEYgate ignores OAuth token — mediumentrypoint.shloops forever waiting only forANTHROPIC_API_KEY:A deployment authenticated via
CLAUDE_CODE_OAUTH_TOKEN(fromclaude setup-token) has noANTHROPIC_API_KEY, so any service withREQUIRE_ANTHROPIC_KEY=1(e.g. thescheduler/telegramservices in the sibling composes) hangs forever.Fix:
Bug 5 — Scheduler UI suggests
make run R=<id>but the image has nomake— lowdashboard/backend/routes/scheduler.pyemits acommandfield of the formmake run R=<id>(surfaced as a copy-pasteable command inScheduler.tsx), but thepython:3.12-slim-based runtime image never installsmake, so the suggested command fails withmake: not found.Fix: emit
uv run python ADWs/routines/<script>(or installmake).Bug 6 —
npxupdater cannot update a Docker/compose install — lowThe updater does
git merge --ff-only, which aborts on any locally-modified tracked file, and its restart path targets a systemd unit /start-services.shthat don't exist in a Docker deployment. So a compose-based install can't be updated through it.Suggested consolidated direction
Introduce a single interpreter resolver used everywhere a routine/hook/handler is launched — prefer the absolute venv path
$WORKSPACE/.venv/bin/pythonwhen it exists, fall through touv run python, never barepython3— and declare all runtime drivers inpyproject.toml. I've applied all of the above locally and confirmed each fix; happy to open a PR if useful.