Skip to content

Docker deploy: hooks crash every turn, DB skills broken, scheduler never runs — all rooted in bare python3 vs uv venv #110

Description

@mariltonleal

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):

  1. 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}'
    )
  2. 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 makelow

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions