Normalize BrainLayer launchd plist hygiene#297
Conversation
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
|
Warning Rate limit exceeded
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 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 configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (14)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
@coderabbitai review |
|
@greptileai review |
|
@codex review |
|
You need to increase your spend limit or enable usage-based billing to run background agents. Go to Cursor |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
There was a problem hiding this comment.
💡 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> |
There was a problem hiding this comment.
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 👍 / 👎.
There was a problem hiding this comment.
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.
0dc7328 to
515f253
Compare
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
|
@codex review |
There was a problem hiding this comment.
💡 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".
|
|
||
| <key>StandardOutPath</key> | ||
| <string>/Users/etanheyman/.local/share/brainlayer/logs/watch.log</string> | ||
| <string>__HOME__/Library/Logs/brainlayer/watch.out.log</string> |
There was a problem hiding this comment.
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 👍 / 👎.
There was a problem hiding this comment.
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.
515f253 to
e783cfd
Compare
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
|
@codex review |
There was a problem hiding this comment.
💡 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".
| <string>--once</string> | ||
| <string>--batch-size</string> | ||
| <string>250</string> |
There was a problem hiding this comment.
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 👍 / 👎.
There was a problem hiding this comment.
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.
e783cfd to
90f28fd
Compare
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
|
@codex review |
There was a problem hiding this comment.
💡 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".
| <key>QueueDirectories</key> | ||
| <array> | ||
| <string>__HOME__/.brainlayer/queue</string> | ||
| </array> |
There was a problem hiding this comment.
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 👍 / 👎.
Summary
com.brainlayer.drainfrom perpetual KeepAlive daemon to event-drivenWatchPaths+--oncewithProcessType=Adaptive.~/Library/Logs/brainlayer/.Before / After
Interactive,KeepAlive=true,/tmplogs, no ExitTimeOut/FD limitInteractive,KeepAlive=true,ExitTimeOut=30,LowPriorityIO=false,~/Library/Logs/brainlayer/*,NumberOfFiles=4096Background,KeepAlive=true, mixed log path, no ExitTimeOut/FD limitBackground,KeepAlive=true+ThrottleInterval=30,ExitTimeOut=120,LowPriorityIO=false, common logs/PATH/FD limitBackground,KeepAlive=true, old local-share logs, no ExitTimeOut/FD limitBackground,KeepAlive=true,ExitTimeOut=30,LowPriorityIO=true, common logs/PATH/FD limitBackground, perpetualKeepAlive=true,--interval 0.5,.brainlayer/logsAdaptive,WatchPaths=~/.brainlayer/queue,--once,ExitTimeOut=60,LowPriorityIO=true, common logs/PATH/FD limitBackground,StartCalendarInterval, no LowPriorityIO/FD limit/PATHBackground,StartCalendarInterval,ExitTimeOut=300,LowPriorityIO=true, common logs/PATH/FD limitBackground, calendar/interval triggers preserved,ExitTimeOut=120,LowPriorityIO=true, common logs/PATH/FD limitInstalled copy verification
launchctl list | rg 'brainlayer'showed BrainBar, enrichment, and watch running; backup/decay/drain/WAL loaded but not perpetually running.launchctl printshowed BrainBar as interactive, watch/enrichment as background, drain as adaptive with watch path, backup as calendar-triggered withkeepalive = 0.Tests
PYTHONPATH=src pytest tests/test_backup_daily.py tests/test_launchd_hygiene.py -qruff 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~/Library/LaunchAgents/com.brainlayer.{brainbar,backup-daily,decay,drain,enrichment,wal-checkpoint,watch}.plistNotes
Note
Medium Risk
Medium risk because it changes launchd job behavior (notably converting
com.brainlayer.drainfrom a KeepAlive polling daemon to an event-drivenWatchPaths+--oncejob), which can affect indexing/enrichment throughput and latency.Overview
Standardizes BrainLayer launchd plists to a consistent “hygiene” baseline: logs move to
~/Library/Logs/brainlayer/,PATHis made explicit (including/usr/sbin+/sbin), and commonExitTimeOut+SoftResourceLimits.NumberOfFiles=4096/LowPriorityIOsettings 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.drainis switched from a perpetualKeepAlivepolling 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.pymatrix 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
/tmpto~/Library/Logs/brainlayer/using__HOME__placeholders, expanded at install time viasedin build-app.sh and install.shEnvironmentVariableswith an expandedPATH(including/usr/sbinand/sbin) across all plists; Python daemons also getPYTHONUNBUFFERED=1SoftResourceLimits.NumberOfFiles=4096and per-daemonExitTimeOutvalues (30s, 60s, or 120s) on all jobsdraindaemon from persistent (KeepAlive) to event-driven, triggered byWatchPaths/QueueDirectorieson~/.brainlayer/queue, running one batch of 250 per invocation withProcessType=Adaptivedrainno longer runs continuously; it fires on queue activity and at load, processing a fixed batch size of 250Macroscope summarized 90f28fd.