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.