---
name: daemon-service-detection-gotcha
description: "Two regressions the v0.3.0 CI caught in daemon-lifecycle service-aware start/stop — service detection must scope to default home; Windows run-refuse must gate on would-vanish, not is_elevated"
metadata: 
  node_type: memory
  type: project
  originSessionId: 183adaa7-7665-46dd-bb77-a1c8691f9f3c
---

The daemon-lifecycle service-aware start/stop (REQ-DAEMON-6/7/8) passed local + manual-rig verification but its FIRST CI run failed `contract_e2e::cold_api_call_autostarts_daemon` on BOTH self-hosted runners. Two distinct regressions — both invisible to a single-home, non-elevated dev box:

1. **Linux — service detection read GLOBAL user config, not the per-invocation home.** `ensure_running` detected the runner's REAL installed `~/.config/systemd/user/spt-daemon.service` (file presence) and shelled to `systemctl --user start` — which serves the DEFAULT home, not the test's `SPT_HOME` override, and fails outright in a bus-less context (`Failed to connect to bus: No medium found`). **Fix:** gate Linux `detected()` on `SPT_HOME` being UNSET — the unit serves only the home it was installed for; a SPT_HOME-overridden invocation spawns a manual daemon. Plus a fallback: if `svc.start()` errors, fall back to `spawn_detached` (autostart must never hard-fail when a spawn would work).

2. **Windows — the `daemon run` elevated-refuse fired on `is_elevated()` alone.** The hfenduleam runner is elevated with NO unelevated identity (`ELEVATED_DAEMON: no unelevated identity to drop to`) — that is the legitimate KH-5.7 "serve elevated, consistent universe" path, not the vanishing one. The blanket refuse broke autostart there. **Fix:** refuse only when a real de-elevation target exists (`deelevate::has_deelevation_target`, mirrors `spawn_deelevated`'s Ok(Some) gate without spawning); the pure decision is `run_refused_when_vanishing(is_windows, would_vanish)`, not `..._when_elevated`.

**General lesson:** any "detect the installed service / global OS state" logic is BLIND on a single-home, non-elevated, has-session-bus dev box. The self-hosted CI runners (elevated, headless, real service installed) are the real test surface — push to a branch and let CI run BEFORE tagging a release. v0.3.0 was tagged prematurely, CI caught it, the tag was deleted + retagged at the fix. See [[kitsubito-linux-rig]] (a manual rig run with a session bus also missed #1 — it had a bus and default home). Fix shipped in v0.3.0 (commit 7f63373).
