Skip to content

Normalize BrainLayer launchd plist hygiene#297

Merged
EtanHey merged 1 commit into
mainfrom
chore/launchd-hygiene
May 18, 2026
Merged

Normalize BrainLayer launchd plist hygiene#297
EtanHey merged 1 commit into
mainfrom
chore/launchd-hygiene

Conversation

@EtanHey
Copy link
Copy Markdown
Owner

@EtanHey EtanHey commented May 18, 2026

Summary

  • Normalize BrainLayer launchd templates across ProcessType, triggers, ExitTimeOut, LowPriorityIO, log locations, PATH, and SoftResourceLimits.
  • Convert com.brainlayer.drain from perpetual KeepAlive daemon to event-driven WatchPaths + --once with ProcessType=Adaptive.
  • Update BrainBar/install helpers so generated installed plists create/use ~/Library/Logs/brainlayer/.

Before / After

Daemon Before After
BrainBar Interactive, KeepAlive=true, /tmp logs, no ExitTimeOut/FD limit Interactive, KeepAlive=true, ExitTimeOut=30, LowPriorityIO=false, ~/Library/Logs/brainlayer/*, NumberOfFiles=4096
enrichment_controller Background, KeepAlive=true, mixed log path, no ExitTimeOut/FD limit Background, KeepAlive=true + ThrottleInterval=30, ExitTimeOut=120, LowPriorityIO=false, common logs/PATH/FD limit
watch Background, KeepAlive=true, old local-share logs, no ExitTimeOut/FD limit Background, KeepAlive=true, ExitTimeOut=30, LowPriorityIO=true, common logs/PATH/FD limit
drain Background, perpetual KeepAlive=true, --interval 0.5, .brainlayer/logs Adaptive, WatchPaths=~/.brainlayer/queue, --once, ExitTimeOut=60, LowPriorityIO=true, common logs/PATH/FD limit
backup_daily Background, StartCalendarInterval, no LowPriorityIO/FD limit/PATH Background, StartCalendarInterval, ExitTimeOut=300, LowPriorityIO=true, common logs/PATH/FD limit
scheduled maintenance mixed/no ProcessType, old local-share logs, no common PATH/FD limit Background, calendar/interval triggers preserved, ExitTimeOut=120, LowPriorityIO=true, common logs/PATH/FD limit

Installed copy verification

  • Rendered and reloaded installed LaunchAgents for BrainBar, backup, decay, drain, enrichment, WAL checkpoint, and watch.
  • launchctl list | rg 'brainlayer' showed BrainBar, enrichment, and watch running; backup/decay/drain/WAL loaded but not perpetually running.
  • launchctl print showed BrainBar as interactive, watch/enrichment as background, drain as adaptive with watch path, backup as calendar-triggered with keepalive = 0.

Tests

  • PYTHONPATH=src pytest tests/test_backup_daily.py tests/test_launchd_hygiene.py -q
  • ruff check src/ tests/ && ruff format --check src/ tests/
  • for f in brain-bar/bundle/com.brainlayer.brainbar.plist launchd/com.brainlayer.watch.plist scripts/launchd/com.brainlayer.*.plist; do plutil -lint "$f" || exit 1; done
  • Installed plist lint for ~/Library/LaunchAgents/com.brainlayer.{brainbar,backup-daily,decay,drain,enrichment,wal-checkpoint,watch}.plist

Notes

  • CodeRabbit CLI first pass found one minor test issue; fixed exact PATH component matching. Second pass was rate-limited.

Note

Medium Risk
Medium risk because it changes launchd job behavior (notably converting com.brainlayer.drain from a KeepAlive polling daemon to an event-driven WatchPaths + --once job), which can affect indexing/enrichment throughput and latency.

Overview
Standardizes BrainLayer launchd plists to a consistent “hygiene” baseline: logs move to ~/Library/Logs/brainlayer/, PATH is made explicit (including /usr/sbin + /sbin), and common ExitTimeOut + SoftResourceLimits.NumberOfFiles=4096/LowPriorityIO settings are added across agents.

Updates installation helpers (brain-bar/build-app.sh, scripts/launchd/install.sh) to create the new log directory and to render __HOME__ placeholders when installing plists. Behaviorally, com.brainlayer.drain is switched from a perpetual KeepAlive polling loop to an event-driven, one-shot run triggered by queue directory changes (WatchPaths/QueueDirectories, ProcessType=Adaptive, --once --batch-size 250).

Adds/updates tests to lock these plist conventions in place, including a new test_launchd_hygiene.py matrix that asserts required PATH components, log destinations, resource limits, and per-daemon expectations.

Reviewed by Cursor Bugbot for commit 90f28fd. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Normalize launchd plist hygiene across all BrainLayer daemons

  • Redirects stdout/stderr for all daemons from /tmp to ~/Library/Logs/brainlayer/ using __HOME__ placeholders, expanded at install time via sed in build-app.sh and install.sh
  • Adds consistent EnvironmentVariables with an expanded PATH (including /usr/sbin and /sbin) across all plists; Python daemons also get PYTHONUNBUFFERED=1
  • Sets SoftResourceLimits.NumberOfFiles=4096 and per-daemon ExitTimeOut values (30s, 60s, or 120s) on all jobs
  • Changes the drain daemon from persistent (KeepAlive) to event-driven, triggered by WatchPaths/QueueDirectories on ~/.brainlayer/queue, running one batch of 250 per invocation with ProcessType=Adaptive
  • Adds test_launchd_hygiene.py to validate PATH, log paths, resource limits, and per-daemon settings
  • Behavioral Change: drain no longer runs continuously; it fires on queue activity and at load, processing a fixed batch size of 250

Macroscope summarized 90f28fd.

Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 18, 2026

Warning

Rate limit exceeded

@EtanHey has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 18 minutes and 54 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d853fbcc-6657-4724-8157-a5492dc7af58

📥 Commits

Reviewing files that changed from the base of the PR and between 50fd1d7 and 90f28fd.

📒 Files selected for processing (14)
  • brain-bar/build-app.sh
  • brain-bar/bundle/com.brainlayer.brainbar.plist
  • launchd/com.brainlayer.watch.plist
  • scripts/launchd/com.brainlayer.backup-daily.plist
  • scripts/launchd/com.brainlayer.decay.plist
  • scripts/launchd/com.brainlayer.drain.plist
  • scripts/launchd/com.brainlayer.enrichment.plist
  • scripts/launchd/com.brainlayer.index.plist
  • scripts/launchd/com.brainlayer.repair-fts.plist
  • scripts/launchd/com.brainlayer.wal-checkpoint.plist
  • scripts/launchd/com.brainlayer.watch.plist
  • scripts/launchd/install.sh
  • tests/test_enrichment_controller.py
  • tests/test_launchd_hygiene.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/launchd-hygiene

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 18, 2026

@coderabbitai review

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 18, 2026

@greptileai review

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 18, 2026

@cursor @BugBot review

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 18, 2026

@codex review

@cursor
Copy link
Copy Markdown

cursor Bot commented May 18, 2026

You need to increase your spend limit or enable usage-based billing to run background agents. Go to Cursor

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 18, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0dc7328677

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


<key>StandardOutPath</key>
<string>__HOME__/Library/Logs/brainlayer-enrichment.log</string>
<string>__HOME__/Library/Logs/brainlayer/enrichment.out.log</string>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Update the validated enrichment log path contract

With this log path change, pytest -q tests/test_enrichment_controller.py::test_enrichment_plist_matches_validated_flex_realtime_profile now fails because the existing validated realtime profile still expects __HOME__/Library/Logs/brainlayer-enrichment.log. In CI contexts that run the enrichment controller tests, this leaves the suite red even though the new launchd hygiene test passes; update the existing assertion/contract alongside this plist change.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Updated the existing enrichment controller contract to expect the new ~/Library/Logs/brainlayer/enrichment.{out,err}.log paths, and reran the targeted enrichment + launchd hygiene tests.

@EtanHey EtanHey force-pushed the chore/launchd-hygiene branch from 0dc7328 to 515f253 Compare May 18, 2026 10:10
Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 18, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 515f25330a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread launchd/com.brainlayer.watch.plist Outdated

<key>StandardOutPath</key>
<string>/Users/etanheyman/.local/share/brainlayer/logs/watch.log</string>
<string>__HOME__/Library/Logs/brainlayer/watch.out.log</string>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restore concrete paths in standalone watch plist

This plist is outside the scripts/launchd/install.sh templating path (repo search only finds tests/docs for launchd/com.brainlayer.watch.plist), so anyone loading this standalone LaunchAgent now gets the literal __HOME__ in paths. launchd treats StandardOutPath as the file path to open rather than expanding custom placeholders, so the watcher can fail to open its logs and also inherit a broken PATH entry unless this file is rendered or kept with concrete/tilde-supported paths.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Restored concrete paths in the standalone watch plist because it is not rendered by install.sh, and adjusted the launchd hygiene test to accept rendered concrete log roots only for standalone/generated plists.

@EtanHey EtanHey force-pushed the chore/launchd-hygiene branch from 515f253 to e783cfd Compare May 18, 2026 10:14
Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 18, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e783cfd19a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +12 to +14
<string>--once</string>
<string>--batch-size</string>
<string>250</string>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep draining bursts until the queue is empty

When a write burst leaves more than 250 *.jsonl files in ~/.brainlayer/queue, this LaunchAgent now runs drain_daemon.py --once --batch-size 250 and then exits. src/brainlayer/drain.py::drain_once slices the work to the first batch_size files, so removing the old KeepAlive/--interval loop means the remaining files can sit indefinitely until some later filesystem change or manual kickstart happens; WatchPaths is not a queue-depth condition and changes made while the job is already running, including the deletes, do not reliably cause another immediate drain. This regresses the durable write queue during busy/locked DB bursts; use a loop-until-empty mode or QueueDirectories semantics here.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Drain now keeps the event-driven --once shape but also declares QueueDirectories on ~/.brainlayer/queue, so launchd will continue relaunching while the queue directory remains non-empty after a partial batch.

@EtanHey EtanHey force-pushed the chore/launchd-hygiene branch from e783cfd to 90f28fd Compare May 18, 2026 10:18
Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@EtanHey
Copy link
Copy Markdown
Owner Author

EtanHey commented May 18, 2026

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 90f28fd685

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +36 to +39
<key>QueueDirectories</key>
<array>
<string>__HOME__/.brainlayer/queue</string>
</array>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep quarantined files out of queued directory

When a malformed *.jsonl is encountered, drain_once renames it to *.jsonl.bad in the same queue directory, then future one-shot runs ignore it because they only glob *.jsonl. With QueueDirectories now watching that directory, any quarantined .bad file leaves the directory non-empty, so launchd keeps relaunching the --once drain only for it to exit immediately until someone manually cleans the queue. This can create a throttled restart loop after the first poison queue item; either move quarantine files outside the watched directory or avoid using directory non-emptiness as the keepalive condition.

Useful? React with 👍 / 👎.

@EtanHey EtanHey merged commit 3dca26a into main May 18, 2026
7 checks passed
@EtanHey EtanHey deleted the chore/launchd-hygiene branch May 18, 2026 10:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant