How to Configure LifeOS Background Jobs Using launchd Service on macOS
LifeOS configures macOS background jobs as launchd agents using plist templates, the DeployComponents pipeline, and a Services.ts wrapper that handles installation, templating, and agent bootstrapping.
LifeOS provides deterministic background automation for macOS through native launchd agents. These jobs handle tasks like work sweeps, data synchronization, and system pulse monitoring. The configuration system relies on template-driven plist generation, environment-aware deployment scripts, and idempotent installation workflows that integrate with the broader LifeOS setup process.
How launchd Configuration Works in LifeOS
The Three-Layer Architecture
LifeOS implements launchd configuration through a coordinated pipeline:
- Enhancement flags in
settings.enhancements.jsoncontrol which jobs are enabled DeployComponents.tsorchestrates the deployment workflowServices.tshandles the low-level plist generation andlaunchctloperations
This separation keeps user configuration simple while ensuring robust, repeatable installations.
Platform Detection
All launchd components are macOS-only. The codebase uses DetectEnv.display—a helper that detects GUI macOS environments—to conditionally skip launchd installation on Linux and Windows. This keeps the Setup workflow idempotent across platforms without manual intervention.
Enabling Background Jobs in settings.enhancements.json
Individual launchd agents are toggled through boolean flags in the enhancements configuration file.
// LifeOS/install/settings.enhancements.json
{
"pulse": true,
"worksweep": true,
"derivedsync": false,
"usageAggregator": true,
"healthsync": false
}
Each key corresponds to a specific background job:
pulse– Dashboard and system heartbeat serviceworksweep– Hourly work environment cleanup and synchronizationderivedsync– Derived data synchronization across contextsusageAggregator– System usage metrics collectionhealthsync– Health data aggregation and sync
When the Setup workflow runs, it reads these flags and constructs a deployment plan containing only enabled components.
The Deployment Pipeline
Step 1: Setup Workflow Invocation
The top-level orchestrator is the Setup workflow located at LifeOS/install/Workflows/Setup.md. Run it via:
# From the repository root
bun run LifeOS/install/Workflows/Setup.md
Step 2: DeployComponents Delegation
Inside DeployComponents.ts (lines 319-321), the pipeline iterates over selected components. For launchd services, it delegates to Services.ts according to the source code comment describing launchd handling:
// LifeOS/install/LIFEOS/TOOLS/Services.ts lines 6-13
// Handles launchd agent installation:
// 1. Reads template from LifeOS/install/LIFEOS/TOOLS/*.plist.template
// 2. Substitutes placeholders ({{HOME}}, {{BUN_PATH}})
// 3. Writes to ~/Library/LaunchAgents/com.lifeos.<label>.plist
// 4. Executes launchctl bootstrap to load into user domain
Step 3: Plist Generation and Installation
The installLaunchd() function performs four operations:
- Template loading – Reads the component-specific
.plist.templatefile - Placeholder substitution – Replaces
{{HOME}}and{{BUN_PATH}}with resolved paths - Plist deployment – Writes to
~/Library/LaunchAgents/com.lifeos.<label>.plist - Agent bootstrap – Executes
launchctl bootstrap "gui/$(id -u)" <plist>to activate
The operation is idempotent—subsequent runs replace existing agents without error.
Plist Template Structure
Each background job ships with a template defining schedule, execution policy, and logging. Here is the worksweep template:
<!-- com.lifeos.worksweep.plist.template -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key><string>com.lifeos.worksweep</string>
<key>ProgramArguments</key>
<array>
<string>{{HOME}}/.claude/LIFEOS/bin/bun</string>
<string>run</string>
<string>LifeOS/install/LIFEOS/TOOLS/worksweep.ts</string>
</array>
<key>RunAtLoad</key><true/>
<key>StartInterval</key><integer>3600</integer> <!-- every hour -->
<key>StandardOutPath</key><string>{{HOME}}/.claude/LIFEOS/MEMORY/OBSERVABILITY/worksweep.log</string>
<key>StandardErrorPath</key><string>{{HOME}}/.claude/LIFEOS/MEMORY/OBSERVABILITY/worksweep.log</string>
</dict>
</plist>
Key launchd directives used across LifeOS templates:
| Directive | Purpose | Typical Value |
|---|---|---|
RunAtLoad |
Execute immediately when agent loads | true |
StartInterval |
Seconds between executions | 3600 (1 hour), 300 (5 min) |
KeepAlive |
Restart if process exits | false (most jobs) |
StandardOutPath / StandardErrorPath |
Log file locations | ~/.claude/LIFEOS/MEMORY/OBSERVABILITY/ |
Installing a Specific Job Manually
For programmatic control or custom workflows, import installLaunchd directly:
import { installLaunchd } from '../LifeOS/install/LIFEOS/TOOLS/Services';
// Install the "worksweep" background job
await installLaunchd('worksweep');
// Install Pulse dashboard service
await installLaunchd('pulse');
This bypasses the full Setup workflow while using the same underlying machinery.
Verifying launchd Agent Status
After installation, confirm proper loading with launchctl:
# General status check
launchctl print "gui/$(id -u)/com.lifeos.worksweep"
# Expected output includes:
# PID = <number>
# ProgramArguments = { "/Users/.../.claude/LIFEOS/bin/bun", "run", "..." }
# State = running
# Scheduled = <interval description>
The Setup workflow performs automatic verification at line 54, running launchctl print for each enabled label and surfacing errors before completion.
Generated Plist Example
After template processing, the resolved plist for worksweep resembles:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key><string>com.lifeos.worksweep</string>
<key>ProgramArguments</key>
<array>
<string>/Users/daniel/.claude/LIFEOS/bin/bun</string>
<string>run</string>
<string>LifeOS/install/LIFEOS/TOOLS/worksweep.ts</string>
</array>
<key>RunAtLoad</key><true/>
<key>StartInterval</key><integer>3600</integer>
<key>StandardOutPath</key><string>/Users/daniel/.claude/LIFEOS/MEMORY/OBSERVABILITY/worksweep.log</string>
<key>StandardErrorPath</key><string>/Users/daniel/.claude/LIFEOS/MEMORY/OBSERVABILITY/worksweep.log</string>
</dict>
</plist>
Bootstrapped agents appear in ~/Library/LaunchAgents/ and persist across reboots until explicitly unloaded.
Summary
- Configuration source:
settings.enhancements.jsoncontains boolean flags for each launchd job - Deployment entry point:
DeployComponents.tsdelegates toServices.tsfor launchd-specific operations - Template processing:
Services.tssubstitutes placeholders and writes plists to~/Library/LaunchAgents/ - Activation:
launchctl bootstrap "gui/$(id -u)" <plist>loads agents into the user domain - Verification:
launchctl print "gui/$(id -u)/com.lifeos.<label>"confirms proper operation - Platform guard:
DetectEnv.displayensures launchd jobs install only on macOS with GUI environments
Frequently Asked Questions
Where are LifeOS launchd plists stored after installation?
LifeOS writes generated plist files to ~/Library/LaunchAgents/ following Apple's standard for user-specific launchd agents. Each file follows the naming convention com.lifeos.<component>.plist, such as com.lifeos.worksweep.plist or com.lifeos.pulse.plist.
Can I modify the schedule of a LifeOS background job?
Yes. Edit the corresponding .plist.template file in LifeOS/install/LIFEOS/TOOLS/, then rerun the Setup workflow. Modify the StartInterval integer (seconds) or replace it with StartCalendarInterval for calendar-based scheduling. Changes take effect after launchctl bootstrap reloads the agent.
What happens if I run Setup multiple times?
The installation is idempotent. Services.installLaunchd() replaces existing plists and re-bootstraps agents without error. The launchctl bootstrap command handles replacement gracefully, and log files append rather than truncate.
How do I temporarily disable a LifeOS launchd job without uninstalling?
Use launchctl directly to unload the agent while preserving the plist:
launchctl bootout "gui/$(id -u)/com.lifeos.worksweep"
To re-enable later:
launchctl bootstrap "gui/$(id -u)" ~/Library/LaunchAgents/com.lifeos.worksweep.plist
Alternatively, set the corresponding flag to false in settings.enhancements.json and rerun Setup to remove the agent entirely.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →