Skip to content

feat(daemon): version handshake, skew warnings, restart-aware serve#182

Merged
ScriptedAlchemy merged 3 commits into
masterfrom
codex/daemon-version-handshake
Jul 2, 2026
Merged

feat(daemon): version handshake, skew warnings, restart-aware serve#182
ScriptedAlchemy merged 3 commits into
masterfrom
codex/daemon-version-handshake

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

Summary

Implements the three daemon-lifecycle follow-ups deferred from #179 (connect-retry grace for daemon restarts).

1. Version field in DaemonHandshake

  • The handshake now carries client_version (the connecting binary's CARGO_PKG_VERSION) with #[serde(default)]: a new daemon parses old handshakes (missing field → empty, never treated as skew), and old daemons ignore the extra field, so mixed-version pairs keep interoperating.
  • The daemon logs a daemon_version_skew event (with a tracedecay daemon restart hint) when a client's version differs, deduped per client version in DaemonEngine — proxy clients reconnect per request, so an undeduped log would flood.

2. Stale daemon on non-systemd paths

  • Client-side detection that works against already-deployed old daemons: the serve stdio proxy reads result.serverInfo.version from proxied initialize responses (daemons of every version send it) and warns on stderr: "daemon is version X but this client is Y — run tracedecay daemon restart".
  • New tracedecay daemon restart subcommand restarts the installed service (reusing refresh_installed_service); when no service is installed it errors with guidance to restart the manual tracedecay daemon run process.
  • tracedecay post-update now distinguishes "no daemon at all" from "an unmanaged daemon is running" and warns loudly in the latter case instead of the quiet "skipping daemon restart" line.
  • Design choice: warn/log rather than daemon self-exit on serving a newer client. A self-exiting daemon is only safe under a supervisor; for a manual tracedecay daemon run it would drop the request mid-flight and leave no daemon behind, which is worse than a stale one. Plain tracedecay upgrade (which skips post-update) is covered by the skew warnings rather than by changing upgrade's semantics.

3. Serve in-process fallback race

  • tracedecay serve startup no longer commits to in-process mode just because the socket file is missing. New should_proxy_serve_to_daemon: if the socket exists → proxy (unchanged fast path); if it's missing but the installed service unit claims exactly this socket (the tracedecay update restart window, where shutdown unlinks the socket before the new daemon rebinds) → probe with the same connect_with_restart_grace used for per-request reconnects; otherwise → in-process immediately, so startup stays instant when no daemon was ever installed. A stopped-but-installed service falls back to in-process after the grace instead of hard-failing every request.

Test plan

  • cargo check --all-targets, cargo clippy --all-targets, cargo fmt --check — clean
  • TRACEDECAY_DATA_DIR=$(mktemp -d) cargo test --lib daemon:: — 47 passed, including new tests:
    • handshake interop: old client → new daemon (missing field parses as empty), new client → old daemon (unknown-field tolerance of the serde derive), version advertised on the wire
    • client_version_skew only flags real mismatches (empty/old-client version is not skew)
    • DaemonEngine logs each skewed client version once
    • daemon_version_skew_warning fires only for mismatched initialize responses and names both versions + the restart command
    • serve transport decision: proxies when socket exists; instant in-process fallback with no socket/service (or a service on a different socket); waits out the restart window when the service owns the socket; falls back after grace when nothing rebinds
  • cargo test --test mcp_cli_serve_test — 14 passed, including new end-to-end regression test: serve started with no socket but an installed unit proxies to a sentinel daemon that binds 400ms later (distinguishable from in-process fallback) and surfaces the version-skew warning on stderr
  • cargo test --test tool_daemon_test --test cli_help_test --test update_plugin_test — all passed

Follow-ups deferred from #179 (connect-retry grace for daemon restarts):

- DaemonHandshake now advertises the client binary version with
  #[serde(default)] so old daemons/clients still interoperate. The daemon
  logs a deduped daemon_version_skew event when a client's version differs.
- The serve stdio proxy reads the daemon's serverInfo.version from proxied
  initialize responses (sent by daemons of every version) and warns on
  stderr when it differs from the client, pointing at the new
  `tracedecay daemon restart` command. `tracedecay post-update` now warns
  loudly when an unmanaged daemon keeps serving the previous version.
- `tracedecay serve` no longer commits to in-process mode just because the
  socket file is missing: when an installed service claims the socket, it
  waits out the daemon-restart window with the same connect grace used for
  per-request reconnects before falling back.
@changeset-bot

changeset-bot Bot commented Jul 2, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: a30ac52

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@ScriptedAlchemy ScriptedAlchemy merged commit 4efd0d1 into master Jul 2, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant