v1.88.0
EvoMap/evolverv1.88.0Jun 3, 2026by autogame-17
AI Summary
Introduces daemon autostart templates for Linux, macOS, and Windows, ensuring the evolver daemon starts automatically on login. Also adds an explicit signal injection channel for user-defined capabilities.
Key Highlights
- Three-platform daemon autostart templates (launchd for macOS, Task Scheduler for Windows).
- Explicit signal injection channel via `<gep-assets-dir>/pending_signals.json`.
- Major overhaul of heartbeat/daemon-survival resilience including singleton-lock takeover.
- Sleep/wake recovery mechanisms and watchdog improvements.
New Features
- Daemon autostart templates for three platforms
- Explicit signal injection channel
Full Release Notes
## [1.88.0] - 2026-06-03
### Added
- **Three-platform daemon autostart templates (#163).** The repo now ships
install helpers so the evolver daemon auto-starts on login with
restart-on-failure on every supported OS, matching the existing Linux
`scripts/evolver.service`: a macOS launchd agent
(`scripts/com.evomap.evolver.plist`, `KeepAlive.SuccessfulExit=false` +
`ThrottleInterval=5` mirroring `Restart=on-failure`/`RestartSec=5`) and a
Windows Task Scheduler installer (`scripts/install-evolver-windows.ps1`,
`-Install`/`-Uninstall`). The Windows shape launches via a generated
`%LOCALAPPDATA%\EvoMap\evolver-task-launcher.vbs` with the node/index paths
baked into VBScript literals and a single quoted task argument — `wscript`
is a Windows-subsystem host so no console window appears on logon, and
`WScript.Quit rc` propagates the exit code so restart-on-failure still
observes failures. All three platforms verified live through a real reboot.
- **Explicit signal injection channel (#172).** A new fifth injection point in
the signal-extraction stage reads user-declared signals from
`<gep-assets-dir>/pending_signals.json` (default `.evolver/gep`) and merges
them into a cycle's signal set, **bypassing the closed 20-entry
`OPPORTUNITY_SIGNALS` vocabulary**. This lets a human-verified, deterministic
capability whose intent no extractor can name still surface as a first-class
signal (previously it collapsed to a generic `capability_gap` and the
dedicated gene could never win selection). File-locked read-once semantics
(`consumePendingSignals()` empties the file after consuming), entries trimmed
and length-capped (≤200), non-fatal on error.
### Fixed
- **Heartbeat / daemon-survival resilience overhaul (#163).** A nine-round
hardening pass on the keep-alive path, addressing failure modes that left the
daemon silently dead after sleep/wake, idle windows, or concurrent startup:
- **Singleton-lock takeover is now atomic** — `linkSync` + `EEXIST` guard
(with a `renameSync` fallback for link-less filesystems), replacing a
non-atomic `unlinkSync`-before-write that could let two daemons both
"own" the lock and rotate `node_secret` against each other into mutual
401 backoff. Lock carries a `lease: true` marker + mtime refreshed every
`LOCK_REFRESH_MS` (60s Win / 120s POSIX); takeover honors both dead-PID
(`kill -0` ESRCH) and lease-expiry (mtime older than `STALE_LOCK_TTL_MS`,
3min Win / 5min POSIX).
- **sd_notify watchdog actually fires under systemd** — shells out to
`systemd-notify(1)` (with `NotifyAccess=all`) instead of the broken
`dgram` `unix_dgram` socket, which threw synchronously on node ≥22 and
swallowed `READY=1`/`WATCHDOG=1`.
- **Sleep/wake recovery** — a wall-clock drift detector (samples `Date.now()`
every 30s, treats a >90s jump as a wake; `cpuUsage`-vs-wall-clock delta
distinguishes CPU throttling from true sleep), a stuck-tick watchdog, and a
wake ritual that drains dead undici connections + restarts SSE.
- **Event-loop anchor + process survival** — a ref'd 10-min keepalive tick
keeps the loop alive and leaves a disk heartbeat for `checkHealth`; EPIPE is
swallowed without killing the process while non-EPIPE stream errors are
still surfaced; `unhandledRejection` consults live heartbeat stats rather
than killing a process mid-recovery.
- **Hostile-hub clamps** — 429 backoff capped at 30min so a malicious/buggy
`retry_after_ms: 86_400_000` can't silence the heartbeat for a day;
`unknown_node` anti-cache-poisoning backoff with an absolute deadline.
- Removed a dead wake hook in `evolver-signal-detect.js` whose relative
`require` was `MODULE_NOT_FOUND` in the deployed `.claude/hooks/` layout
(no in-proc consumer exists, so it was a no-op that cost a 3000-line
`require` on every IDE edit).
- **Standalone-binary obfuscation retries (#171).** Default `OBF_MAX_ATTEMPTS`
raised 4 → 12 in `scripts/build_binaries.js`. The obfuscation step
occasionally mangles `new.target` into an illegal `#target` token,
non-deterministically across node processes; the seed-perturbation retry is
the right mechanism but 4 attempts wasn't enough (the v1.87.4 deploy hit 4/4
and aborted before npm publish + binary upload). No behavior change on
first-attempt success; env override unchanged.