Skip to content

Installer: blind HOME→/home substitution corrupts settings & source; Pulse not operational on Linux #1484

Description

@docxology

Summary

Two related problem clusters surfaced while (re)installing LifeOS 6.0.5 on Linux (Parrot/Debian, Claude Code harness). One is a data-corrupting installer bug; the other is Pulse not being operational on Linux. Both are fixed on the affected machine; this issue documents root causes and proposes upstream fixes.


Problem 1 — Installer performs a blind HOME/home/<user> substitution that corrupts config and source

Symptoms

  • settings.json contained "$/home/<user>/.claude/hooks/…" for every hook command, the statusline, and env vars (LIFEOS_DIR, LIFEOS_CONFIG_DIR, PROJECTS_DIR). A path beginning $/home/... is invalid (the shell can't expand $/), so all hooks and the statusline silently failed to execute.
  • Three permissions.deny safety rules were defanged: Bash(rm -rf $HOME) had become Bash(rm -rf $/home/<user>), which no longer matches a real rm -rf $HOME.
  • 146 TypeScript source files under LIFEOS/ + hooks/ were corrupted inside code, e.g. MemoryHealthCheck.ts:
    const /home/<user> = process.env./home/<user> || "";   // was: const HOME = process.env.HOME
    → hard syntax errors; the tools wouldn't parse or run.

Root cause

A substitution pass replaced the bare token HOME with the literal home path ($HOME$/home/<user>, const HOMEconst /home/<user>) — the classic sed 's|HOME|/home/<user>|g' failure mode. The enabling weakness is still present in Tools/InstallEngine.ts:

  • TEMPLATE_EXTENSIONS includes .ts, .json, .sh, .toml, so substituteTree() rewrites source and settings.
  • substituteTree() substitutes via naive after.split(placeholder).join(value) for any key — so a caller passing a bare "HOME" placeholder rewrites every HOME substring across code.

Fix applied

Hardened substituteTree() to only substitute properly-delimited {{TOKEN}} placeholders:

if (!/^\{\{[A-Z0-9_]+\}\}$/.test(placeholder)) continue;

Verified: passing {HOME: "/home/<user>"} now leaves const HOME/$HOME untouched while {{LIFEOS_VERSION}} still resolves. Also repaired the live tree (settings.json $/home/<user>$HOME; re-deployed the 146 corrupted source files from the pristine install/ payload).

Proposed upstream fix

  • Ship the substituteTree() braced-token guard above.
  • Consider dropping .ts from TEMPLATE_EXTENSIONS (source files shouldn't be templated).

Problem 2 — Pulse is not operational on Linux

INSTALL.md/setup treat Pulse as a macOS-launchd component; on Linux it neither auto-starts nor fully runs. Specific gaps:

  1. No Linux service — Pulse is only wired via launchd (com.lifeos.pulse.plist). Nothing starts it on Linux, and it doesn't survive reboot.
  2. Dashboard + all APIs 404PULSE/node_modules is missing yaml, so Observability/observability.ts fails to import → observabilityModule is null → every page and /api/* route 404s while /healthz can still read files. yaml is imported by observability but absent from PULSE/package.json.
  3. settings.system.json shipped without a hooks block, yet MemoryHealthCheck.ts treats it as the hook source of truth and flags the memory hooks (MemoryReviewTrigger/Fire, MemoryHealthGate) as a "regression source" (they exist only in the generated settings.json). InstallHooks writes hooks into the generated settings.json, which MergeSettings regenerates — an architectural mismatch between "InstallHooks owns hooks" and "settings.system.json is the source of truth."
  4. assistant-tasks cron fails foreverPULSE.toml enables a job running bun run Assistant/checks/tasks.ts, but the Assistant/ module isn't in the payload → perpetual failures → persistent degraded health.
  5. LIFEOS_CONFIG_DIR semantics conflict — install tools (ScaffoldUser/LinkUser/SeedPulse) treat it as the data home whose /USER is the tree (~/.config/LIFEOS), while runtime tools (Grok.ts, YouTubeApi.ts, llcli.ts) treat it as the dir containing .env. The two only reconcile if .env and USER/ share a dir.
  6. Standalone install tools depend on unset env — running ScaffoldUser/LinkUser directly (as the AI-install doc instructs) with LIFEOS_CONFIG_DIR unset/misset caused them to write into a literal $-named directory.

Fix applied (Linux)

  • bun add yaml in PULSE/ → observability module loads; /, /agents, /api/algorithm now serve 200.
  • Built the Next.js dashboard export (cd PULSE/Observability && bun install && bun run build).
  • Disabled the assistant-tasks job in PULSE.toml and cleared its stale consecutiveFailures from PULSE/state/state.json/healthz = ok.
  • Synced the full clean hook set into settings.system.json and set LIFEOS_CONFIG_DIR = $HOME/.config/LIFEOS (matches the real data home).
  • Added a systemd user service (~/.config/systemd/user/lifeos-pulse.service) + loginctl enable-linger → Pulse runs on boot and restarts on failure. Verified ok across systemctl --user restart.

Proposed upstream fix

  • Add yaml to PULSE/package.json dependencies.
  • Ship settings.system.json with the system hooks block (or point MemoryHealthCheck at the real source), reconciling the InstallHooks/MergeSettings ownership model.
  • Guard optional cron jobs (e.g. assistant-tasks) when their module isn't present, instead of failing every tick.
  • Ship a systemd unit template for Linux alongside the launchd plists, and have setup install+enable it on Linux (with enable-linger).
  • Document/standardize LIFEOS_CONFIG_DIR semantics (data-home vs .env-home).
  • Make ScaffoldUser/LinkUser resolve their paths robustly (or fail loudly) when env vars are unset in standalone runs, rather than writing to a literal $ dir.

Environment

  • OS: Linux (Parrot Security 7 / Debian-based), x64
  • Harness: Claude Code; bun 1.3.10
  • LifeOS: 6.0.5
  • Config root: ~/.claude; data home: ~/.config/LIFEOS

Investigated and remediated via an assisted reinstall session; happy to open PRs for the substituteTree guard, the yaml dependency, and the systemd unit.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions