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
- Have hooks daemon v2.27.0 installed
- 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:
- Layer 1 (
upgrade.sh) — performs git checkout v3.1.0 on the daemon repo, then delegates to Layer 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.
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
What Happens
The upgrade reports success with
✅ Post-install checks passed, but the daemon immediately fails to start:Root Cause
The upgrade process has two layers:
upgrade.sh) — performsgit checkout v3.1.0on the daemon repo, then delegates to Layer 2upgrade_version.sh) — checks the current git ref, sees it is already at v3.1.0, and takes the idempotent pathThe idempotent path (the
if [ "$ROLLBACK_REF" = "$TARGET_VERSION" ]branch inupgrade_version.sh) redeploys hooks, settings,.gitignore, and skills — but does not callrecreate_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 syncis never run. The venv retains the packages from v2.27.0, and any new dependencies added in v3.1.0 (in this casemdformat) are never installed.Secondary Issue
The post-install check reports
✅ Post-install checks passedeven 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'spyproject.toml/uv.lock, regardless of which upgrade path is taken.Environment
uv: not installed on PATH (installed into~/.local/binbut not present in this environment)Suggested Fix
In
scripts/upgrade_version.sh, the idempotent path should include arecreate_venv "$DAEMON_DIR"call (or at minimumuv sync) before restarting the daemon, to ensure dependencies are always in sync with the checked-out version.