Skip to content

Claude/daemon install loop glaloq#43

Merged
SteveWeed79 merged 5 commits into
mainfrom
claude/daemon-install-loop-glaloq
Jul 18, 2026
Merged

Claude/daemon install loop glaloq#43
SteveWeed79 merged 5 commits into
mainfrom
claude/daemon-install-loop-glaloq

Conversation

@SteveWeed79

Copy link
Copy Markdown
Owner

What & why

How was it tested?

Checklist

  • pytest passes
  • ruff check palctl tests passes
  • Commits are signed off (git commit -s) — opening this PR agrees to the
    CLA for its
    contributions (see
    CONTRIBUTING.md)
  • User-visible changes have a line under [Unreleased] in
    CHANGELOG.md

claude added 5 commits July 17, 2026 23:17
Re-running the daemon install over an already-running service wrote the
new unit file / exe / params but never re-launched the process, so the
old binary and settings kept running: `systemctl start` no-ops on an
active unit and `nssm start` no-ops on a running service.

Linux install now uses `systemctl restart` (starts if stopped,
re-launches if running); Windows stops the service before starting so
the freshly-set Application/AppParameters take effect. Added runner tests
pinning both paths.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MCtUhn1rJhuh9k1qVMr31H
start_detached() skipped the spawn whenever a daemon was already
answering on the control port, so re-running setup with Windows login
startup left the OLD daemon (old build, old config) running — the same
reinstall gap just fixed for the service paths.

It now replaces the running daemon, in the order that's safe: remove any
leftover daemon *service* registration first (its manager would resurrect
a killed daemon and fight the fresh one over the port, and would
double-start it at the next boot), then stop whatever process is
listening on the daemon port via the existing terminate->kill ladder,
then spawn. The port scan never targets our own process, non-listeners,
or other ports.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MCtUhn1rJhuh9k1qVMr31H
…hing it

An in-place re-install could inherit stale settings from the old
registration: `nssm install` no-ops on an existing service and the
per-setting `set` calls only overwrite what the new install specifies —
so empty args skipped the AppParameters set (a dev install's
`-m palctl.daemon` survived into a frozen re-install and broke it), and
ObjectName was never reset back to LocalSystem after an --as-user
install.

install_service now goes stop -> remove -> register -> start whenever the
service already exists, so the registration is exactly what this install
specifies. Fresh installs skip the removal. This also supersedes the
previous stop-before-start fix: the service is never left running across
the re-registration.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MCtUhn1rJhuh9k1qVMr31H
Re-running setup with a different background-startup choice left the
previous mechanism behind:

- login -> service kept the HKCU Run key, so the next login spawned a
  second daemon that fought the service over the control port; and the
  fresh service couldn't bind the port while the old login-startup
  daemon still held it, so NSSM restart-looped the new daemon while the
  old one kept serving. install_service now drops the Run key, registers
  with start=False, clears the port, then starts.
- Unticking the background group ("none") did nothing at all. It now
  removes both mechanisms and stops the running daemon — the same
  "unticking actually turns it off" contract the Discord toggle
  documents.
- Switching AWAY from a registered daemon service needs admin to remove
  it; preflight now asks for elevation in that case instead of letting
  the removal fail silently and claiming success.
- The wizard pre-selects "Windows service" when that's what is
  registered, instead of silently defaulting back to login startup on a
  re-run.
- Linux: a stray non-service daemon (dev checkout run by hand) is
  stopped before the unit starts instead of crash-looping it; the unit's
  own daemon is left to systemctl restart.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MCtUhn1rJhuh9k1qVMr31H
…I install-startup complete

Gap 1 — `palctl-daemon install-startup` on the CLI only wrote the Run
key, which takes effect at the NEXT login, leaving an old daemon (or a
leftover service) serving until then. It now runs the same replacement
start_detached does for setup: remove a leftover daemon service, stop
the old daemon, start the new one immediately. When the service can't be
removed (not elevated), start_detached now verifies the removal and
reports failure with the admin-prompt fix printed, instead of killing a
process the service manager would resurrect and pretending it worked.

Gap 2 — the wizard always defaulted the background-startup choice to
"login", silently switching a "service" or "none" choice back on
re-runs, because nothing distinguished "chose none" from a first run.
Setup now persists the choice in config (daemon_startup: login/service/
none; "" = pre-upgrade config, which falls back to probing the
registered service), and the wizard restores it — including unticking
the background group for "none".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MCtUhn1rJhuh9k1qVMr31H
@SteveWeed79
SteveWeed79 merged commit 27eb20d into main Jul 18, 2026
16 checks passed
@SteveWeed79
SteveWeed79 deleted the claude/daemon-install-loop-glaloq branch July 18, 2026 00:13
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.

2 participants