Skip to content

v3.1.0 upgrade: idempotent path skips venv recreation, leaving new dependencies uninstalled #28

@ballidev

Description

@ballidev

Summary

Upgrading from v2.27.0 to v3.1.0 leaves the daemon broken with ModuleNotFoundError: No module named 'mdformat' because the upgrade process never installs the new dependency.

Steps to Reproduce

  1. Have hooks daemon v2.27.0 installed
  2. Run the upgrade skill to upgrade to v3.1.0

What Happens

The upgrade reports success with ✅ Post-install checks passed, but the daemon immediately fails to start:

✗ Daemon failed to start (exit code 1)

Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  ...
  File "/workspace/.claude/hooks-daemon/src/claude_code_hooks_daemon/daemon/cli.py", line 41, in <module>
    import mdformat
ModuleNotFoundError: No module named 'mdformat'

✗ Failed to start daemon
⚠  Daemon restart had issues; check status manually
→ Running post-installation verification checks...
✅ Post-install checks passed       ← incorrect: daemon is not running
✓ Upgrade verification complete

Root Cause

The upgrade process has two layers:

  1. Layer 1 (upgrade.sh) — performs git checkout v3.1.0 on the daemon repo, then delegates to Layer 2
  2. Layer 2 (upgrade_version.sh) — checks the current git ref, sees it is already at v3.1.0, and takes the idempotent path

The idempotent path (the if [ "$ROLLBACK_REF" = "$TARGET_VERSION" ] branch in upgrade_version.sh) redeploys hooks, settings, .gitignore, and skills — but does not call recreate_venv (Step 7 in the normal upgrade path).

Because Layer 1 already checked out the new tag before Layer 2 runs, Layer 2 always sees "already at target version" and always takes the idempotent path, meaning uv sync is never run. The venv retains the packages from v2.27.0, and any new dependencies added in v3.1.0 (in this case mdformat) are never installed.

Secondary Issue

The post-install check reports ✅ Post-install checks passed even though the daemon failed to start moments earlier. The verification is not correctly detecting the failed daemon state.

Expected Behaviour

The upgrade process should always run uv sync (or equivalent) to ensure the venv matches the new version's pyproject.toml/uv.lock, regardless of which upgrade path is taken.

Environment

  • Previous version: v2.27.0
  • Target version: v3.1.0
  • OS: Linux (container, Fedora-based)
  • Python: 3.11.2
  • uv: not installed on PATH (installed into ~/.local/bin but not present in this environment)

Suggested Fix

In scripts/upgrade_version.sh, the idempotent path should include a recreate_venv "$DAEMON_DIR" call (or at minimum uv sync) before restarting the daemon, to ensure dependencies are always in sync with the checked-out version.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions